c++20协程不自动调度或启动线程,co_await/co_return仅触发编译器生成的状态机;卡住主因是promise_type未配齐或coroutine_handle未持有/未手动resume。

直接说结论:C++20 的 co_await 和 co_return 本身不启动线程、不调度、不自动 resume,它们只是编译器生成状态机的触发点;你写对了关键字,但协程不跑——大概率是 promise_type 没配对,或 coroutine_handle 没被持有/没手动 resume。
为什么 co_await 表达式挂起后就卡住不动?
co_await 不是“等系统完成”,而是走三步协议:await_ready() → 若返回 false,调用 await_suspend(handle) → 然后真挂起。常见卡死现象:你用了 std::suspend_always{},但它不自动唤醒,必须靠外部调用 handle.resume() 才能继续。
- 别指望
co_await std::suspend_always{}自己恢复——它只负责挂起,唤醒全靠你 - 如果
await_suspend返回void或true,协程挂起后立即返回到调用方,handle必须被安全持有(比如用std::shared_ptr包一层),否则 resume 时访问已销毁内存会崩溃 - 返回
false表示“我自行处理唤醒”,此时你得确保在某个时刻调用handle.resume(),且不能在栈上临时构造 handle 后就让它析构
co_return 后怎么拿到返回值?
返回值不通过栈传递,也不存在函数局部变量里——它被存进 promise_type 的成员变量中,由 co_return expr 触发 return_value(expr) 写入。所以你必须让协程返回对象(如 Task)持有 std::coroutine_handle<promise_type></promise_type>,才能在之后读取 handle.promise().value。
- 没定义
return_value(T),co_return 42会编译失败;写成return_void()却又带值,也会报错 -
Task对象若只在栈上短暂存在,而你延迟访问handle.promise().value,可能读到未初始化或已被覆盖的垃圾值——因为 promise 对象生命周期绑定于协程帧,帧释放后数据失效 - 若用
co_return抛异常,但unhandled_exception()是空实现,程序直接std::terminate(),不会传播异常
promise_type 最小必要接口有哪些?
缺一不可。编译器生成协程代码时,硬性要求这些函数存在,哪怕只做最简实现。少一个,连编译都过不去。
-
get_return_object():必须返回协程句柄关联的对象(如Task{handle}),不能返回临时对象 -
initial_suspend():决定协程一创建就运行(std::suspend_never{})还是先挂起(std::suspend_always{});设错会导致“函数调用完就结束”,根本看不到co_await效果 -
final_suspend():影响资源清理时机。设为std::suspend_always{}可让你在协程结束后读取返回值或捕获异常;设为std::suspend_never{}则协程帧立即释放,promise 成员不可再访问 -
return_void()或return_value(T):按协程是否带返回值二选一,不能两个都不写 -
unhandled_exception():建议至少写成std::terminate(),否则异常会静默吞掉
coroutine_handle 拿到后为什么一 resume 就崩溃?
崩溃主因是 coroutine_handle 指向的协程帧(含 promise 和局部变量)已被释放,但你还试图 resume 它。协程帧默认堆分配,但谁负责 delete?没人管——除非你在 final_suspend 里显式回收,或用 RAII 封装。
- 不要把
coroutine_handle存在栈上并跨作用域使用;更别把它传给另一个线程后,原线程立刻退出作用域 - 如果
final_suspend()返回std::suspend_always{},你必须自己调用handle.destroy()释放帧;漏掉就会内存泄漏 - 用
std::coroutine_handle::from_address()从裸指针重建 handle 时,必须确保地址确实指向有效协程帧,否则行为未定义
真正难的不是写对语法,而是理解:协程帧的生命周期、promise 成员的存活期、handle 的所有权归属——这三者一旦错位,表现就是随机崩溃或读到垃圾值,调试时很难关联到根源。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











