std::coroutine_handle<void> 仅支持 resume/suspend/destroy,无法访问 promise;typed handle(如 std::coroutine_handle<Promise>)才能调用 promise() 安全读写状态。漏写模板参数会导致 promise() 失败,且编译器不报错。

std::coroutine_handle 和 typed handle 的区别在哪
直接用 std::coroutine_handle<void></void> 只能做最基础的 resume/suspend/destroy,没法安全访问协程帧里的 promise 对象;而 std::coroutine_handle<promisetype></promisetype> 才能调用 promise() 拿到 promise 实例,进而读写状态、传递返回值或异常。漏掉模板参数是常见错误——编译器不会报错,但 promise() 调用会失败。
-
std::coroutine_handle<void></void>适合仅作生命周期管理(比如调度器中排队、统一销毁) - 需要与 promise 交互时,必须用具体 promise 类型实例化,例如
std::coroutine_handle<mypromise></mypromise> - 从
co_await表达式或promise.get_return_object()返回的 handle 通常是 typed 版本,别手动 cast 成void版本丢信息
resume() 前必须确保协程处于 suspend 状态
对刚构造出来的 handle(比如从 promise 返回的对象)直接调用 resume() 是未定义行为——它可能还没开始执行,也可能已结束。标准做法是:只对明确 suspend 在某个 await_point 的 handle 调用 resume()。
- 首次启动协程用
coroutine_handle::operator()()或显式resume(),但前提是协程尚未运行(即处于 initial_suspend 状态) - 检查是否可 resume:用
h.done() == false且协程当前挂起(可通过 promise 中标记的状态辅助判断) - 重复 resume 同一个已结束的 handle 会 crash,
done()是唯一可靠的终止状态判断依据
destroy() 必须由协程自己或 owner 主动触发
协程帧内存默认由 operator new 分配,destroy() 不仅释放内存,还会调用 promise 的析构函数和 final_suspend 后的清理逻辑。如果忘了调用,就会内存泄漏;如果在 resume 后立即 destroy,可能破坏 promise 的状态流转。
- 不要依赖 RAII 自动调用 ——
coroutine_handle析构时不自动 destroy,必须显式调用 - 典型模式:在 promise 的
final_suspend()返回std::suspend_always{}后,在 awaiter 的await_resume()里或外层控制流中调用handle.destroy() - 多线程环境下,确保 destroy 只被调用一次,且不在协程正在执行时调用(否则竞态)
如何安全地跨线程转移 coroutine_handle
std::coroutine_handle 是可复制的轻量值类型,但本身不带同步语义。跨线程传递后,接收方必须保证:1)原线程不再访问该 handle;2)目标线程在 resume 前确认协程处于可恢复状态。
立即学习“C++免费学习笔记(深入)”;
- 推荐用
std::shared_ptr包裹 handle + promise 数据(需自定义分配器或手动管理生命周期),避免裸 handle 传递 - 若用队列传递,务必配合原子 flag 或 mutex 保护
done()和resume()的时序 - 注意:不同线程 resume 同一协程时,promise 内部状态(如
value_、exception_)必须是线程安全的,标准 promise 不提供内置保护
destroy() 的责任归属和 done() 的使用时机。很多崩溃源于在协程已结束(done() == true)后仍尝试 resume(),或在 final_suspend 之后忘记调用 destroy()。


















