await_ready应诚实反映就绪状态:已知结果时必须返回true以避免冗余挂起,异步操作才返回false;错误地总返回false会导致无谓开销,且await_suspend中不可放置必需初始化逻辑。

await_ready 为什么总返回 false 很可能不对
await_ready 的语义是“当前 awaitable 是否已经就绪,无需挂起”,它不是“要不要挂起”的开关,而是对执行状态的诚实判断。常见错误是无脑返回 false,导致每次 co_await 都强制走挂起路径,白白开销。比如一个立即完成的 awaitable(如包装 std::optional<t></t> 的值),若内部已有结果,await_ready 就该返回 true;否则调度器会绕过 await_suspend,直接调用 await_resume。
实操建议:
- 如果 awaitable 构造时已确定结果(如
make_ready_awaitable(42)),await_ready必须返回true - 若依赖异步操作(如 I/O、定时器),通常返回
false,但要确保后续await_suspend能真正触发回调或注册等待 - 返回
true时,await_suspend**完全不会被调用**,别在里面放必需的初始化逻辑
await_suspend 怎么安全传入 coroutine_handle 并避免悬垂
await_suspend 的参数是 std::coroutine_handle,代表被挂起的协程本身。你得用它来安排恢复——比如存进队列、交给线程池、或注册到 epoll/kqueue。关键陷阱在于:这个 handle 是临时的,不能长期裸存;一旦协程被销毁(如异常退出未 resume),再调用 resume() 就是未定义行为。
实操建议:
- 不要用裸指针或引用保存
std::coroutine_handle;优先用std::coroutine_handle<promise>::from_address(...)</promise>搭配自定义 promise 获取上下文 - 若需延迟恢复,用
std::shared_ptr包裹 handle(配合自定义 deleter 确保只 resume 一次),或转成void*存进 C API(如 libuv)后,恢复前用std::coroutine_handle::from_address还原 - 返回
true表示“我已接管恢复”,协程保持挂起;返回false表示“请立即 resume”,此时await_resume紧接着执行;返回std::coroutine_handle则交由该 handle 恢复(常用于链式调度)
await_resume 的返回类型必须匹配 co_await 表达式结果
await_resume 的返回值类型,直接成为 co_await expr 整个表达式的类型。如果它声明为 int,但实际抛出异常或返回 std::string,编译失败。更隐蔽的问题是移动语义:若返回大对象(如 std::vector<int></int>),没写成右值引用返回,可能触发不必要拷贝。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
实操建议:
- 返回类型应与 awaitable 所承诺的结果一致,例如网络读取 awaitable 的
await_resume应返回std::size_t(字节数)或std::error_code - 若可能失败,别用
throw,改用返回std::expected<t std::error_code></t>——这样调用方能统一处理 - 返回局部对象时,用
return std::move(local_obj);显式触发移动,避免编译器优化失效
自定义 awaitable 必须满足可复制性或显式禁用
协程在挂起前可能对 awaitable 对象做复制(尤其在某些编译器实现或调试模式下)。如果你的 awaitable 内含唯一资源(如 std::unique_ptr、文件描述符),又没禁用拷贝,就会出现双重释放或句柄泄漏。
实操建议:
- 若 awaitable 不可复制(比如绑定了某个 socket fd),必须显式删除拷贝构造/赋值:
Awaitable(const Awaitable&) = delete; - 若支持复制(如纯状态机对象),确保深拷贝所有资源,或用引用计数(
std::shared_ptr)管理共享状态 - 用
static_assert(std::is_move_constructible_v<awaitable>);</awaitable>在类定义后检查,比运行时报错更早暴露问题
真正难的不是写出三个函数,而是让它们在协程生命周期的每个分支(正常完成、异常传播、取消、多线程并发恢复)下都保持状态一致。尤其是 await_suspend 返回 true 后,谁负责调用 resume()、在什么线程、是否可能重复调用——这些细节一旦漏掉,就是偶发崩溃或死锁。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










