在 C++20 标准中,协程(coroutines)成为语言的一部分,为异步编程提供了更自然、更高效的语法。协程的核心是 co_await、co_yield 和 co_return,这些关键字背后隐藏着一套完善的生成器与调度框架。本文将从协程的生成器、状态机、栈管理、异常处理以及协程对象生命周期四个方面,剖析 C++20 协程的内部实现原理。
1. 协程函数与生成器对象
1.1 语法与返回类型
协程函数的返回类型必须满足 std::suspend_always 或 std::suspend_never 的接口,或者是实现了 promise_type 的自定义类型。典型的协程返回类型是 `std::generator
` 或 `std::future`。编译器通过 `promise_type` 与协程函数的生成器对象进行绑定。
### 1.2 生成器对象的生命周期
编译器会为协程函数生成一个 **隐藏的 `promise_type`** 结构体,协程对象与 `promise_type` 通过 `std::coroutine_handle` 关联。生成器对象在堆上分配(或栈上分配,取决于实现),其生命周期由调用者持有的 `coroutine_handle` 控制。当协程完成时,`handle.destroy()` 会释放资源。
—
## 2. 状态机与状态存储
协程的实现相当于将函数体拆分成一系列 **状态块**,每个 `co_await`/`co_yield`/`co_return` 处切换到下一个状态。
### 2.1 状态机表
编译器会生成一个 **状态机表**,记录每个暂停点对应的 `label`。在执行时,协程通过 `switch (state)` 或者 **跳转表** 实现状态切换,避免了大量的 `if` 或者递归调用。
### 2.2 Promise 与返回值
– `promise_type::get_return_object()`:返回协程的生成器对象(如 `std::generator
`)。
– `promise_type::initial_suspend()` / `promise_type::final_suspend()`:决定协程是否在入口处暂停,或者在退出时暂停。
– `promise_type::return_value()`:捕获 `co_return` 产生的返回值。
– `promise_type::unhandled_exception()`:捕获协程内部异常。
—
## 3. 栈与寄存器的管理
### 3.1 栈分配策略
协程的实现需要在函数返回前保持其 **调用栈**。大多数实现采用 **“协程栈”** 或 **“状态机栈”**,即把原来的栈帧拆分成一组可序列化的数据结构。
– **堆栈式协程**:将每个协程实例的数据存放在堆上,使用 `std::aligned_storage` 或自定义内存池。
– **轻量级协程**:如 Boost.Context 或 libtask,使用 **用户级线程**(ucontext)技术,将寄存器上下文存储在结构体中。
### 3.2 寄存器保存
当协程在 `co_await` 或 `co_yield` 处暂停时,编译器会把当前 **CPU 寄存器**(如 `%rax`, `%rbx` 等)保存到 `promise_type` 或 `coroutine_frame` 结构中。恢复时,按逆序恢复寄存器,确保协程继续执行时的上下文完整。
—
## 4. 异步与事件循环
协程通常与 **异步事件循环** 配合使用。C++20 标准本身并不提供事件循环框架,但协程与 `std::experimental::generator`、`std::future` 或第三方库(如 Boost.Asio、libuv)配合实现。
### 4.1 `co_await` 的实现
– **Awaitable 对象**:需要实现 `await_ready()`, `await_suspend(handle)`, `await_resume()` 三个成员函数。
– **`await_suspend`**:如果返回 `true`,协程将挂起;如果返回 `false`,协程立即继续。
– **`await_resume`**:当协程恢复时,返回的值。
异步 I/O 库将 `co_await` 与 I/O 事件关联,事件发生时通过回调调用 `handle.resume()` 重新激活协程。
—
## 5. 例子:简单协程实现
“`cpp
#include
#include
template
struct generator {
struct promise_type {
T current_;
std::suspend_always yield_value(T value) {
current_ = 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
h_;
explicit generator(std::coroutine_handle
h) : h_(h) {}
~generator() { if (h_) h_.destroy(); }
bool move_next() { return h_.resume(); }
T current_value() const { return h_.promise().current_; }
};
generator
countdown(int start) {
for (int i = start; i >= 0; –i) {
co_yield i;
}
}
int main() {
auto gen = countdown(5);
while (gen.move_next()) {
std::cout