深入理解C++20协程的工作原理

C++20 引入了协程(coroutines)这一强大的语言特性,使得异步编程、生成器和协作式多任务调度变得更直观。本文将从协程的概念、核心实现原理、使用场景以及常见陷阱四个方面,对 C++20 协程进行系统性解读,并给出完整代码示例。

1. 协程到底是什么?

协程是一种轻量级的、可挂起与恢复的函数。与传统的同步函数不同,协程可以在执行过程中“暂停”并保存状态,随后再恢复继续执行。这样,协程能够在不阻塞线程的情况下,实现异步流程、延迟计算以及无限流的生成。

在 C++20 中,协程的语法核心由以下四个关键词组成:

关键词 功能
co_await 暂停协程直到 awaitable 对象准备好
co_yield 暂停协程并返回一个值给调用者
co_return 结束协程并返回最终结果
co_return void 结束协程但不返回值

协程的返回类型必须是一个协程类型(如 `std::generator

`、`std::task` 或自定义 `promise_type`),而不是普通返回值。 ## 2. 内部工作原理 ### 2.1 协程状态机 编译器会把协程函数展开成一个状态机。每个 `co_await`、`co_yield` 或 `co_return` 位置对应一个状态。执行时,协程会从当前状态切换到下一个状态,必要时保存执行上下文。 ### 2.2 Promise 与 Awaiter – **Promise**:协程内部的“承诺”,负责管理协程的生命周期、异常、返回值等。它拥有 `promise_type`,并在协程启动时自动创建。 – **Awaiter**:用于实现 `co_await` 的对象。它必须实现 `await_ready()`, `await_suspend()`, `await_resume()` 三个成员函数。 典型的 `co_await` 工作流程: 1. `await_ready()` 判断是否已经就绪。若返回 `true`,立即调用 `await_resume()`。 2. 若返回 `false`,执行 `await_suspend()`,该函数接受协程句柄 `std::coroutine_handle` 并决定是否挂起协程。 3. 当 awaitable 变为就绪时,调度器或事件循环会调用协程句柄的 `resume()`,继续执行。 ### 2.3 句柄与内存管理 – **std::coroutine_handle**:指向协程的句柄,用来挂起、恢复或销毁协程。句柄可以是 `promise_type::promise_type` 的内部句柄,也可以是用户手动创建。 – **内存**:协程的堆栈(存储局部变量)由编译器分配在堆上,而不是栈上,避免栈溢出。用户可通过 `std::allocator` 自定义分配器。 ## 3. 常见使用场景 ### 3.1 异步 I/O 使用 `co_await` 与异步 I/O 库(如 Boost.Asio、libuv 或自定义事件循环)配合,可以实现非阻塞网络通信: “`cpp #include #include #include struct AwaitableTimer { std::chrono::milliseconds duration; std::coroutine_handle h; bool await_ready() const noexcept { return duration.count() == 0; } void await_suspend(std::coroutine_handle handle) { h = handle; // 这里启动计时器,计时结束后调用 resume() std::thread([this]{ std::this_thread::sleep_for(duration); h.resume(); }).detach(); } void await_resume() const noexcept {} }; struct AsyncTask { struct promise_type { AsyncTask get_return_object() { return {}; } std::suspend_never initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception() { std::terminate(); } }; }; AsyncTask async_print() { std::cout #include template struct Generator { struct promise_type { T current_value; std::suspend_always yield_value(T value) { current_value = value; return {}; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } Generator get_return_object() { return Generator{ std::coroutine_handle ::from_promise(*this)}; } void return_void() {} void unhandled_exception() { std::terminate(); } }; std::coroutine_handle handle; explicit Generator(std::coroutine_handle h) : handle(h) {} ~Generator() { if (handle) handle.destroy(); } T next() { handle.resume(); return handle.promise().current_value; } }; Generator natural_numbers() { int i = 1; while (true) co_yield i++; } int main() { auto gen = natural_numbers(); for (int i = 0; i

发表评论