C++如何实现简单的协程任务包装器_C++20从零实现co_await逻辑【源码】

4次阅读

c++20协程核心是promise_type与awaitable协议:promise_type必须提供get_return_object、initial_suspend、final_suspend;awaitable需实现await_ready、await_suspend、await_resume;coroutine_handle须安全持有,不可裸存或跨生命周期使用。

C++如何实现简单的协程任务包装器_C++20从零实现co_await逻辑【源码】

协程任务包装器必须实现 promise_type

没有 promise_typeco_await 就无法挂起、恢复或获取返回值。C++20 协程不是语法糖,而是编译器与用户约定的一套接口协议,其中 promise_type 是核心契约。

它至少要提供三个成员函数

  • get_return_object():返回你定义的任务类型(比如 Task),让 co_await 表达式能拿到可等待对象
  • initial_suspend():决定协程启动时是否立即挂起(通常返回 suspend_always{} 实现懒启动)
  • final_suspend() noexcept:决定协程结束时是否挂起(必须返回 suspend_always{}suspend_never{};若返回 suspend_always,后续需手动销毁帧)

漏掉任意一个,编译器会报类似 no member named 'get_return_object' in 'MyTask::promise_type' 的错误。

awaitable 对象得有 await_ready/await_suspend/await_resume

你写的任务类型(如 Task)本身不是可等待的,必须通过 operator co_await() 返回一个满足 awaitable 要求的对象——即具备这三个函数:

立即学习C++免费学习笔记(深入)”;

  • await_ready():返回 bool,指示是否可直接继续(比如已就绪的值可返回 true 避免挂起)
  • await_suspend(coroutine_handle

    h)

    :关键逻辑所在。你要在这里保存 h(即当前协程句柄),并安排它何时被恢复(例如放进线程池、定时器队列、或直接调用 h.resume() 实现同步返回)

  • await_resume():协程恢复后,这里返回给 co_await 表达式的值(类型必须匹配 T

注意:await_suspend 如果返回 void,表示你已自行调度恢复;如果返回 booltrue 表示挂起,false 表示继续执行;如果返回另一个 coroutine_handle,编译器会直接跳转到那个协程。

coroutine_handle 不能裸存、不能跨生命周期使用

这是最常踩的坑:把 coroutine_handle 存在局部变量里,或者不加防护地在线程间传递,极易导致悬垂句柄(dangling handle)和未定义行为。

  • 协程帧(frame)分配在上(除非显式用 std::coroutine_traits<...>::operator new 拦截),但生命周期由你管理;coroutine_handle 只是轻量指针,不负责释放
  • 一旦协程已结束(final_suspend 后),再调用 resume() 或访问其 promise() 就是 UB
  • 若需异步恢复,务必确保:① 句柄被正确持有(如用 std::shared_ptr 包裹任务对象);② 恢复前检查 handle.done() == false

常见症状:程序偶发崩溃、segmentation fault、或 coroutine_handle::resume(): the coroutine is already complete 这类运行时断言失败。

不要试图绕过标准协程 ABI 手写 co_await 逻辑

有人想“从零实现 co_await”,其实是误解了 C++20 协程的设计意图。co_await 本身是编译器内置机制,不可重载或替换;你能控制的,只是它背后调用的那套 promise/awaitable 接口。

真正该做的,是实现干净的 promise_type 和 awaitable 类型,而不是去碰汇编、帧布局或编译器生成的 __builtin_coro_* 内建函数。后者不仅无文档、不稳定,而且不同编译器(Clang/GCC/MSVC)差异极大。

如果你发现需要读取或修改协程帧内存布局、或依赖某个特定编译器的挂起点编号——说明设计已偏离正轨。协程的可移植性和可维护性,全系于是否严格遵循 promise/awaitable 协议。

text=ZqhQzanResources