C++ 23 标准中的协程:从语法到实践

协程(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

发表评论