std::suspend_always是供await_suspend返回的标记类型,非可直接co_await的awaitable;需自定义类型实现三个await_*函数,其中await_ready返回false、await_suspend返回std::suspend_always{}且不调用resume,才能实现可控挂起。

std::suspend_always 是个空挂起点,不是“挂起指令”
它本身不触发挂起,只是告诉编译器:这个 await_suspend 函数返回 std::suspend_always{} 时,协程应该暂停执行并移交控制权。真正挂起发生在协程函数体执行到 co_await 表达式、且其 awaitable 的 await_suspend 返回 std::suspend_always 之后。
怎么写一个能被 std::suspend_always 挂住的 awaitable
你需要自己定义一个类型,实现 await_ready、await_suspend、await_resume 三函数。关键点在于:
-
await_ready返回false,否则根本不会调用await_suspend -
await_suspend返回std::suspend_always{}(或void+std::suspend_always作为返回值) - 不要在
await_suspend里手动 resume,否则挂起立即结束,形同虚设
示例:
struct suspend_always_awaiter {
bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<> h) const noexcept {
// 不调用 h.resume(),也不做任何调度
// 就让它停在这儿
}
void await_resume() const noexcept {}
};
用法:co_await suspend_always_awaiter{}; —— 协程会在此处永久挂起(除非外部手动 resume)。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
std::suspend_always 和手动 resume 的配合方式
挂起后协程状态是 suspended,必须靠保存的 std::coroutine_handle 来恢复。常见错误是手抖忘了存 handle,或者 resume 调用时机不对:
- 在
await_suspend中接收的std::coroutine_handle<>必须显式保存(比如塞进全局队列、成员变量、智能指针),否则离开函数就丢弃了 -
resume()只能对已挂起(suspended)且未被销毁的 handle 调用;对已 resume 过的 handle 再调用是未定义行为 - 跨线程 resume 需确保 handle 的生命周期安全,不能在另一线程 resume 后还访问原协程栈变量
典型漏掉的一步:co_await 后没保存 handle,导致后续无法 resume —— std::suspend_always 本身不提供存储能力。
为什么不用 std::suspend_always 直接 co_await std::suspend_always{}?
因为 std::suspend_always 是个空结构体,没有实现三个 awaiter 成员函数,直接 co_await std::suspend_always{} 会编译失败(no member named 'await_ready')。它只是个“标记类型”,专供 await_suspend 返回使用,不是 awaitable 本体。
标准库中真正可直接 co_await 的是 std::suspend_never 和 std::suspend_always 的“替身”——但 C++20 标准并未提供开箱即用的挂起 awaitable。你得自己写,或者用第三方库(如 cppcoro 的 cppcoro::static_thread_pool::schedule() 类间接效果)。
最易忽略的点:挂起不是目的,可控恢复才是。只管 co_await 却不管理 handle 生命周期,协程就真“永远睡过去了”。

















