C++20协程是可扩展底层框架,非开箱即用的async/await;必须正确定义promise_type三要素,co_await按序查找awaiter,跨线程唤醒需通过任务队列投递handle,std::generator不支持异步IO。

标准 C++20 协程不是“实现协程机制”的工具,而是提供了一套可扩展的底层协程框架——它不直接给你 async/await 语义,也不自带调度器或线程池。你必须自己实现 promise_type、coroutine_handle 管理逻辑和挂起点行为,否则编译器只生成一堆无法运行的骨架代码。
为什么 co_await 编译通过却运行崩溃?
最常见原因是未正确定义 promise_type 中的 get_return_object()、initial_suspend() 和 unhandled_exception()。C++20 要求这三者必须存在且签名正确,缺一即导致未定义行为(如空指针解引用、栈破坏)。
-
get_return_object()必须返回一个能持有coroutine_handle的对象(如自定义Task类),不能只返回void或裸 handle -
initial_suspend()若返回suspend_always{},但后续没调用handle.resume(),协程就永远卡住;若返回suspend_never{},则需确保 promise 构造完成前不发生异常 -
unhandled_exception()不实现会导致异常穿透栈帧,直接std::terminate()
co_await 表达式背后真正调用了哪些函数?
对任意表达式 expr 执行 co_await expr,编译器按顺序尝试查找并调用:
- 若
expr自身有operator co_await()成员函数,调用它,得到 awaiter 对象 - 否则,若存在
operator co_await(expr)非成员重载,调用它 - 否则,直接使用
expr作为 awaiter(要求它具备await_ready()/await_suspend()/await_resume()三个成员)
注意:await_suspend() 返回 void 表示同步恢复;返回 bool 表示是否已自行调度(true = 不恢复,false = 立即恢复);返回 coroutine_handle 则将控制权转交给该 handle。
立即学习“C++免费学习笔记(深入)”;
如何用 std::coroutine_handle 安全跨线程唤醒协程?
协程挂起后,其栈帧通常位于当前线程栈上(除非用了堆分配的 promise),因此 coroutine_handle::resume() 必须在**同一栈上下文**中调用,否则触发未定义行为。安全跨线程唤醒的唯一合规路径是:
- 在
await_suspend()中获取目标线程的事件循环句柄(如io_context、自定义任务队列) - 把
coroutine_handle包装成任务对象,投递到目标线程的任务队列 - 目标线程从队列取出后,在自身栈上安全调用
handle.resume()
切勿在线程 A 中保存 handle,然后在线程 B 中直接调用 resume()——即使加锁也无效,这是栈生命周期问题,不是竞态问题。
为什么不用 std::generator 直接写异步 HTTP 请求?
std::generator 是为同步迭代器场景设计的:它隐含假设每次 co_yield 后立即被消费者调用 next(),且整个生命周期单线程、无挂起等待。它没有 co_await 支持,不能等待文件描述符就绪、不能交出控制权给 IO 多路复用器。试图强行混用会导致:
-
generator对象析构时自动调用destroy(),可能中断正在进行的网络读写 - 无法在
co_yield前插入非阻塞等待逻辑(如epoll_wait) - 缺乏异常传播通道,网络错误只能靠外部状态轮询,违背协程的“线性错误处理”初衷
真要写异步 HTTP,得从零实现一个带 IO 调度能力的 Task 类型,其中 promise_type 内聚了 epoll/kqueue/IOCP 封装和超时管理——这才是 C++20 协程的真实使用姿势。









