C++20协程是编译器支持+标准库协作的非对称协程机制,核心为promise、awaiter和coroutine_handle三要素,底层基于状态机与stackless协程帧,不提供对称跳转原语。

协程在 C++20 中是通过编译器支持 + 标准库协作实现的,它不是语言内置的“运行时调度器”,而是一套可挂起、可恢复的函数机制,其底层本质是状态机 + 堆栈管理 + 编译器生成的控制流跳转代码。C++ 标准只定义了非对称协程(asymmetric coroutine),所谓“对称协程”在标准中并不存在,是其他语言(如 Kotlin、Go)或第三方库(如 libco、Boost.Coroutine2)的概念,需自行模拟。
协程的核心三要素:promise、awaiter、coroutine_handle
C++ 协程的可挂起行为由三个关键组件协同完成:
- promise 对象:每个协程实例在堆上(或栈上,取决于分配策略)构造一个 promise_type 实例,负责生命周期管理、异常处理、返回值封装、挂起点决策(initial_suspend / final_suspend)等;
- awaiter 对象:实现 await_ready / await_suspend / await_resume 三个成员函数的对象,决定何时挂起、挂起时做什么(如注册回调、保存上下文)、恢复后返回什么;
- coroutine_handle:轻量级句柄,指向协程帧(coroutine frame)首地址,用于手动恢复(resume)、销毁(destroy)、查询状态(done);它不拥有资源,仅是访问入口。
协程帧(coroutine frame):编译器生成的状态机内存布局
当你声明一个 co_await / co_yield / co_return 的函数时,编译器会:
- 将函数体拆解为多个状态(state),对应不同挂起点和恢复点;
- 把局部变量、参数、临时对象、promise 对象、awaiter 对象(若按值捕获)一并打包进一块连续内存——即“协程帧”;
- 帧头部固定存放 coroutine_handle 可识别的元数据(如 vtable 指针、promise 指针、当前状态码);
- 默认使用 operator new 分配帧内存(可被自定义 promise::get_return_object_on_allocation 覆盖),也可用 stackful 方式(如 Boost)做栈切换,但标准协程是 stackless(无独立栈,复用调用方栈帧)。
非对称协程:单向控制流与明确的挂起/恢复语义
C++20 协程是典型的非对称设计:
立即学习“C++免费学习笔记(深入)”;
- 协程只能由“外部”(比如调用者或 awaiter)主动 resume,不能自己直接跳转到另一个协程;
- co_await 表达式挂起当前协程,并把控制权交还给 awaiter::await_suspend 返回的 handle(通常是调用方或调度器);
- 没有 yield-to 或 transfer_to 等对称跳转原语;要实现“协程 A 切到协程 B”,必须经由第三方调度器中转(例如 event loop 保存 A 的 handle,再 resume B);
- 这种设计简化了 ABI 和内存模型,避免栈共享、寄存器保存等复杂问题,也更契合异步 I/O 和 futures 模型。
所谓“对称协程”在 C++ 中如何模拟?
标准不提供,但可通过以下方式逼近:
- 用 boost::context 或 libco 手动管理栈和寄存器上下文,实现任意协程间跳转(swap_context);
- 基于 coroutine_handle + 自定义调度器,在 await_suspend 中不返回 void,而是返回另一个协程的 handle,并在调度器中触发 resume;
- 封装 transfer() 成员函数,内部保存当前 handle,然后 resume 目标 handle,形成逻辑上的“协程 A 主动切到 B”;
- 注意:这类模拟无法绕过 stackless 限制,若需真正独立栈(stackful),必须脱离标准协程机制,使用汇编或第三方上下文库。









