因为std::coroutine_handle不管理状态和唤醒逻辑,仅是轻量包装;需手动确保协程处于suspend状态、内存有效且无并发resume,否则易触发未定义行为。
std::coroutine_handle 手动调度?因为 std::coroutine_handle 本身不带状态管理或唤醒逻辑,它只是个“指针+虚表”的薄包装。你拿到一个 std::coroutine_handle,调用 .resume() 前必须确保协程处于 suspend 状态、内存未被释放、且没有被并发 resume —— 这些都得自己兜底。
典型错误现象:std::coroutine_handle::resume(): cannot resume an already-resumed or destroyed coroutine,往往是因为忘了检查 .done(),或在 await_suspend() 里误把 handle 存到栈上又提前返回。
std::deque 避免迭代器失效)关键不是实现全部接口,而是只保留调度器真正需要的三件事:构造时注册、挂起时移交控制权、销毁时清理资源。不需要 return_value、unhandled_exception 等——除非你打算支持 co_return 或异常传播。
struct simple_promise {
simple_coro get_return_object() {
return simple_coro(std::coroutine_handle::from_promise(*this));
}
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() noexcept { std::terminate(); }
// 调度器靠这个把协程加进 ready 队列
void return_to_scheduler() {
scheduler::instance().push(handle);
}
std::coroutine_handle handle;
};
注意 handle 成员必须是 public,且在 get_return_object() 中立即保存;否则后续 resume 时无法访问自身 handle。这是容易被忽略的细节:promise 对象和协程帧是绑定的,但 handle 必须显式存一份才能在 final_suspend 或自定义 awaiter 里用。
立即学习“C++免费学习笔记(深入)”;
核心是写一个自定义 awaiter,其 await_suspend() 把当前协程 handle 推入调度器队列,并返回 false 表示不自动 resume(即彻底移交调度权)。
常见错误:返回 true 或 std::coroutine_handle{},导致协程被立即 resume,调度器完全没机会插手。
await_ready() 返回 false 强制走 suspend 流程(简化模型,实际可按需判断)await_suspend(h) 中调用 scheduler::instance().push(h),然后返回 false
await_resume() 只需返回 void,因为本例不传递值struct schedule_awaiter { bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle<> h) noexcept { scheduler::instance().push(h); } void await_resume() const noexcept {} };
最简调度器只需一个 while 循环 pop + resume,但真实场景下必须处理:协程 resume 后可能立刻再次 suspend、可能抛异常、可能 self-destruct(比如 co_return 后进入 final_suspend)。如果调度器不检查 handle.done() 就继续 pop,会 crash。
性能影响:每次 pop 都应从容器头部取(std::deque::front()),避免 vector 的 O(n) 移动;若用 std::queue 包装 deque,记得它默认用 deque 作底层容器,没问题。
!h.done(),否则 final_suspend 后再 resume 是未定义行为h.done(),为 true 则跳过 push 回队列(已结束)unhandled_exception 已接管,强行捕获反而掩盖问题void scheduler::run() {
while (!ready_queue.empty()) {
auto h = std::move(ready_queue.front());
ready_queue.pop_front();
if (h.done()) continue;
h.resume();
if (!h.done()) {
push(h); // 若仍活跃,放回队尾(轮转调度)
}
}
}
final_suspend 返回 std::suspend_always 是关键:它让协程停在最后一步,把销毁控制权交还给调度器。否则协程帧可能被 runtime 自动释放,而你的调度器还拿着 dangling handle。