C++20协程不带调度器,需手动实现调度逻辑;co_await仅为挂起/恢复语法糖,不决定恢复时机与执行者;最简FIFO调度器需管理句柄存取与resume,并严格控制生命周期。

C++20 协程本身不带调度器,必须手动实现调度逻辑——这是所有初学者最容易误以为“协程自带线程/队列”的关键误区。
为什么不能直接 co_await 就自动跑起来?
因为 co_await 只是挂起/恢复的语法糖,它不决定“谁来恢复”“何时恢复”。编译器生成的状态机默认把控制权交还给调用者,而调用者(比如 main())通常不会主动去 resume 你。没有调度器,协程就只是个一动不动的句柄。
常见错误现象:
- 调用一个返回
task的协程函数后,程序立刻退出,什么都没打印 - 用
std::suspend_always{}挂起,但没人调用handle.resume(),协程永远卡住 - 多个协程注册进队列,但只 run 了一次就停了,没轮转
根本原因:C++20 协程是无栈、懒启动、完全用户控制的——调度器是你写的,不是语言给的。
立即学习“C++免费学习笔记(深入)”;
最简 FIFO 调度器怎么写?
核心就三件事:存句柄、取句柄、resume 句柄。不需要锁(单线程),也不需要复杂状态管理(初始/终挂起全设为 std::suspend_always)。
实操建议:
- 用
std::queue<:coroutine_handle>>存待执行协程 - 协程 promise_type 中,
initial_suspend()返回std::suspend_always{},确保创建后不自动执行 -
final_suspend()也返回std::suspend_always{},让协程结束时不销毁,方便调度器回收或判断完成 - 调度器
run()方法里循环pop + resume,并在 resume 后检查handle.done()决定是否丢弃
示例片段(promise_type 关键部分):
struct task_promise {
task get_return_object() { return task{this}; }
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() { std::terminate(); }
};协程 handle 怎么安全入队和出队?
协程句柄是轻量值类型,可拷贝、可移动,但一旦 resume() 或 destroy() 过,再访问就是未定义行为。所以调度器必须严格管理生命周期。
容易踩的坑:
- 把局部协程的 handle 直接 push 到全局队列——协程栈帧可能已析构(错!promise 和局部变量都在堆上分配,只要没 destroy 就安全)
- resume 后没及时 pop,导致重复 resume(崩溃或逻辑错乱)
- 协程内部抛异常但
unhandled_exception()没处理,resume()后直接 terminate
正确做法:
- 在
get_return_object()中返回一个包装对象(如task),内部持有std::coroutine_handle并负责最终destroy() - 调度器
run()中 resume 前先if (!h.done()) h.resume();,resume 后立即if (h.done()) h.destroy();
什么时候该用 std::suspend_never?
std::suspend_never 表示“协程创建即执行,直到第一个挂起点”,适合那些想立刻开始、但中间仍需异步等待的场景(比如初始化 IO、发请求)。但它会让调度器“错过”协程启动时机——因为你没机会把它塞进队列。
使用场景:
- 协程体第一行是同步计算,之后才
co_await网络响应(这时用initial_suspend() = suspend_never合理) - 你明确不想让调度器接管启动阶段,而是由业务代码显式触发(比如按钮点击后才运行)
但注意:final_suspend() = suspend_never 是危险操作——协程结束会自动 destroy(),你无法拦截、统计、清理资源。除非你 100% 确保 promise 对象生命周期完全受控,否则一律用 suspend_always。
真正的难点不在语法,而在「谁在什么时候持有 handle、谁负责 destroy、谁保证不 double-resume」——这些都得靠你写的调度器逻辑兜底,C++20 不替你做任何假设。









