协程(coroutine)是 C++20 开始被正式纳入标准的强大特性,它提供了比传统线程和回调更简洁、可读性更高的异步编程方式。C++23 在此基础上做了进一步的完善和优化,让协程的使用更加方便。本文将从语法、关键概念、实现细节以及实际案例四个方面,系统梳理 C++23 协程的核心内容,帮助读者快速上手。
1. 语法与基本结构
1.1 关键字和基本形态
C++23 协程与 C++20 的语法保持一致,核心关键词依旧是 co_await, co_yield, co_return,以及 co_resume(用于手动恢复)。协程函数的返回类型需要是 协程返回类型(co-routine return type),常见的包括 `std::generator
`、`std::task` 等。
“`cpp
std::generator
count_up_to(int n) {
for (int i = 1; i ` 与 `std::suspend_always` / `std::suspend_never` 的简易实现。
“`cpp
struct my_promise {
int current = 0;
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
int get_return_object() { return current; }
void unhandled_exception() { std::terminate(); }
void return_void() {}
};
“`
### 1.3 协程的创建与销毁
协程的创建由编译器生成 `__coro` 结构体完成,调用协程函数返回的是对应的 Promise 对象包装器。销毁过程会调用 `final_suspend` 并释放资源。
—
## 2. 协程的核心概念
### 2.1 协程状态
协程的状态主要包括:
– **Suspended**:已暂停,等待恢复。
– **Running**:正在执行。
– **Completed**:已完成,无法再恢复。
协程在 `co_await`、`co_yield` 或 `co_return` 处暂停,随后由外部或内部恢复。
### 2.2 Awaitable 与 Awaiter
`co_await` 后面的表达式必须满足 *awaitable* 接口。C++23 提供了默认的 `std::suspend_always` / `std::suspend_never`,也支持自定义 Awaiter。
“`cpp
struct async_task {
bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle h) const noexcept {
// 异步操作开始,完成后恢复协程
}
int await_resume() const noexcept { return 42; }
};
int main() {
std::coroutine_handle h = my_coroutine(); // 简化示例
int result = co_await async_task(); // 这里会暂停
}
“`
### 2.3 协程的异常处理
异常会沿着协程的调用链向上传递,直到到达 `return` 或 `co_return`。如果协程未捕获异常,Promise 的 `unhandled_exception` 会被调用。
—
## 3. C++23 对协程的改进
### 3.1 更强的协程返回类型
C++23 引入了 `std::generator
` 与 `std::task`,分别对应可生成多值和单值的协程。`std::generator` 在迭代器模型上兼容标准容器,使得协程可以像普通 `range` 一样使用。
“`cpp
std::generator lines(const std::string& path) {
std::ifstream file(path);
std::string line;
while (std::getline(file, line)) {
co_yield line;
}
}
“`
### 3.2 `std::suspend_always` 与 `std::suspend_never` 的默认化
C++23 允许在协程返回类型中使用 `std::suspend_always` 或 `std::suspend_never` 作为默认暂停策略,减少模板代码量。
### 3.3 更细粒度的资源管理
通过 `std::coroutine_handle` 的 `destroy()` 方法以及 Promise 的 `final_suspend`,可以更精细地控制协程资源的释放,避免泄漏。
—
## 4. 实践案例:异步文件读取
下面演示一个使用 C++23 协程实现的异步文件读取框架。
“`cpp
#include
#include
#include
#include
#include
#include
#include
// 简化的 awaitable,用于模拟异步 I/O
struct async_read_line {
std::ifstream& file;
std::string line;
bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle h) const noexcept {
std::thread([this, h]() {
if (std::getline(file, line)) {
h.resume(); // I/O 完成后恢复协程
} else {
h.resume(); // EOF,直接恢复
}
}).detach();
}
std::optional await_resume() const noexcept {
if (line.empty()) return std::nullopt;
return line;
}
};
std::generator async_file_reader(const std::string& path) {
std::ifstream file(path);
if (!file) throw std::runtime_error(“Cannot open file”);
while (true) {
auto maybe_line = co_await async_read_line{file};
if (!maybe_line) break; // EOF
co_yield *maybe_line;
}
}
int main() {
try {
for (const auto& line : async_file_reader(“sample.txt”)) {
std::cout