await_suspend的返回值决定协程是否挂起及恢复方式:void立即挂起需手动resume;bool中false继续执行、true挂起;coroutine_handle则自动转移控制权实现尾调用。

await_suspend 的返回值决定是否手动恢复协程
在 C++20 协程中,await_suspend 不是“传递” coroutine_handle 的入口,而是由编译器调用它,并把当前协程的 coroutine_handle 作为唯一参数传入。你无法从外部“塞”一个 handle 给它——它的职责是决定:这个协程接下来要不要暂停?如果暂停,该由谁来恢复?
关键点在于返回值类型:
- 返回
void:协程立即挂起,后续必须靠外部显式调用resume()(比如从线程池、IO 完成回调里) - 返回
bool:true表示挂起,false表示不挂起(继续执行下一行) - 返回另一个
coroutine_handle:协程挂起,并**自动将控制权转移给该 handle**(即“尾调用优化”,不增加栈帧)
想让其他协程恢复当前协程?保存 handle 到 awaiter 成员里
如果你需要在异步操作完成时恢复当前协程(比如网络读完触发 resume),就得在 await_suspend 中拿到 coroutine_handle 并存起来。常见做法是把 handle 存进 awaiter 对象自身或关联的 promise/状态机中。
例如:
struct async_read_awaiter {
socket& s;
std::vector<char> buf;
std::coroutine_handle handle; // ← 专门存住它
bool await_ready() { return false; }
void await_suspend(std::coroutine_handle h) {
handle = h; // ← 就是这里:保存传入的 handle
s.start_read(buf.data(), buf.size(), [this](size_t n) {
// IO 完成回调里调用:
handle.resume(); // ← 恢复原协程
});
}
int await_resume() { return static_cast<int>(buf.size()); }
};</int></char>
返回 coroutine_handle 的场景:避免额外 resume 调用开销
当你的 awaiter 逻辑上只是“委托给另一个协程执行”,且希望跳过中间挂起/唤醒步骤(比如实现 co_await inner_coro 的直接链式调度),就让 await_suspend 返回那个目标 coroutine_handle。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
此时编译器会直接跳转过去,不压栈、不调度调度器、也不触发 await_resume —— 这是真正的协程链式调用。
注意限制:
- 返回的 handle 必须是 valid 的(不能是
done()状态) - 不能返回当前协程自己的 handle(否则死循环)
- 目标协程必须尚未 resume 过(否则未定义行为)
容易踩的坑:handle 泄露、重复 resume、跨线程未同步
coroutine_handle 是轻量但危险的裸指针语义对象。常见错误包括:
- 在
await_suspend中只存了handle,但 awaiter 对象被销毁了,导致回调里resume()崩溃 - IO 回调触发多次(比如底层没禁用重复通知),造成同一 handle 被
resume()多次 → UB - 回调发生在其他线程,而
handle.resume()不是线程安全的(C++20 标准未保证),需加锁或用原子状态标记是否已 resume - 在
await_suspend返回void后,忘记在任何地方调用resume()→ 协程永久挂起(泄漏)
最隐蔽的问题是:resume 发生在非创建协程的线程上,而协程栈可能绑定了 TLS 或特定内存池——这时候光 resume 不够,还得确保上下文切换正确。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










