
std::coroutine_handle::resume() 为什么不能随便调?
协程不是线程,resume() 不是“唤醒”,而是“继续执行到下一个挂起点或结束”。如果协程已结束(done() 返回 true),再调 resume() 是未定义行为——常见表现是段错误或静默崩溃,尤其在 Release 模式下极难复现。
- 必须在调用前检查
h.done() == false,且该检查和resume()之间不能有其他协程调度干扰(比如多线程并发时需加锁或用原子状态标记) - 不要依赖
operator bool()判断是否可 resume:它只检查 handle 是否非空,不反映协程是否存活 - 若协程因异常退出,其 promise 对象可能已被析构,此时
h.promise()会访问非法内存——务必确保 handle 生命周期严格晚于 promise 析构
如何安全获取并持有 promise 对象?
std::coroutine_handle<p>::promise()</p> 返回的是引用,不是副本。一旦协程结束,promise 对象通常随帧栈一起被销毁(除非你手动 new 出来并管理)。直接保存这个引用长期使用,等于持有一根悬垂指针。
- 若需跨生命周期访问 promise 数据,应在 promise 类型中显式提供堆分配支持(例如重载
operator new,并在get_return_object_on_allocation_failure中处理失败) - 更稳妥的做法是:在协程结束前(如在
final_suspend的 awaiter 中),把关键状态复制到外部对象,再通过std::shared_ptr或自定义句柄包装器持有 - 注意:不同编译器对 promise 对象的析构时机略有差异——Clang 可能在 final_suspend 后立即析构,GCC 可能延迟到 handle 被销毁时;别写依赖具体顺序的逻辑
手动控制协程内存布局时,operator new/delete 怎么配对?
协程帧(coroutine frame)默认由编译器在 operator new 分配的内存上构造。如果你重载了 promise 的 operator new,就必须同时提供匹配的 operator delete,否则帧析构时会调用错的 delete,引发 double-free 或内存泄漏。
- 必须保证:promise 类型的
operator new和operator delete成对出现,且签名完全一致(包括 noexcept、参数个数与类型) - 若使用 placement-new 手动构造 promise(例如在预分配缓冲区中),则绝不能让编译器自动生成帧的析构逻辑——需在
final_suspend返回的 awaiter 中显式调用promise.~P(),并确保后续不再访问该内存 - MSVC 对协程帧的 operator delete 调用更激进,有时会在未调用
destroy()前就尝试释放内存;建议统一用std::coroutine_handle::destroy()主动终结,而非依赖自动清理
std::coroutine_handle::destroy() 调用后还能干啥?
destroy() 会触发 promise 析构、释放协程帧内存,并使 handle 进入无效状态。此后任何对 handle 的操作(包括 done()、promise()、甚至再次 destroy())都是未定义行为。
立即学习“C++免费学习笔记(深入)”;
- 调用
destroy()后应立即将 handle 置为std::coroutine_handle{}(即空 handle),避免误用 - 不要在 promise 的析构函数里再调用
destroy()——这是循环调用,GCC 会报错,Clang 可能静默崩溃 - 若协程处于 suspended 状态但尚未 destroy,其帧内存仍有效,可安全读取 promise 字段;但只要调过
destroy(),这块内存就不可再碰,哪怕只是打印地址
printf 或日志可能改变内联行为,从而掩盖悬垂访问问题。真要验证,得关优化、开 ASan、并且用 release 模式交叉验证。








