c++20协程是编译器级特性,需用co_await/co_yield/co_return声明,返回满足promise_type要求的类型,并提供await_ready/suspend/resume三函数才能被co_await;std::coroutine_handle非线程安全,需谨慎管理生命周期。

协程在 C++20 里不是“实现出来”的,是用 co_await、co_yield、co_return 写出来的
标准协程不是库函数,也不是你手动写调度器就能“实现”的。C++20 协程是编译器级支持:你写带 co_await 的函数,编译器自动生成状态机和挂起/恢复逻辑。想“手撸协程”?那是 C++17 或更早的 hack(比如 Boost.Coroutine2),但那不是标准协程,也不兼容 std::coroutine_handle。
常见错误现象:error: use of undeclared identifier 'co_await' —— 没开 C++20 标准,或没加 -std=c++20(Clang/GCC)或 /std:c++20(MSVC)。
- 必须返回一个满足
promise_type要求的类型(如std::future、task<t></t>自定义类) - 函数体里至少出现一次
co_await、co_yield或co_return,否则编译器不视为协程 - 不能在普通函数里直接
co_await;必须在协程函数内,且被 awaitable 类型支持
怎么让一个类型能被 co_await?关键看它有没有 await_ready、await_suspend、await_resume
不是所有对象都能 co_await。编译器遇到 co_await expr,会按顺序查:expr.operator co_await() → operator co_await(expr) → 如果 expr 是类类型,查它是否有这三个成员函数。
使用场景:封装异步 I/O、定时器、线程池任务提交等。比如你想 co_await sleep_for(1s),就得让 sleep_for 返回的对象提供上述三函数。
立即学习“C++免费学习笔记(深入)”;
-
await_ready()返回bool:立刻完成就返回true,跳过挂起 -
await_suspend(handle)接收std::coroutine_handle:你要在这里决定“挂起后去哪”,比如投递到线程池、注册到 epoll、或直接 resume(即同步执行) -
await_resume()返回协程继续执行时拿到的值,可带异常传播
std::coroutine_handle 不是线程安全的,别在线程间裸传
它只是一个轻量指针(通常 8 字节),指向协程帧(coroutine frame)。它的 resume() 和 destroy() 必须由持有方确保不并发调用,否则 UB。
性能影响:频繁跨线程 resume 会导致缓存行失效、虚假共享,比单纯传递数据代价高得多。
- 如果要从其他线程唤醒协程,务必用原子操作或锁保护 handle 生命周期(比如用
std::shared_ptr包一层,或用std::atomic<:coroutine_handle>></:coroutine_handle>存储) - 调用
resume()后,该 handle 不再有效(除非你显式 copy);调用destroy()后,整个协程帧释放,不能再 resume - MSVC 下 debug 模式可能额外检查 handle 是否已 destroy,release 模式则完全不检查 —— 容易漏掉 double-resume
别指望协程自动解决线程安全问题
协程切换不等于线程切换。co_await 可能在同一线程上恢复(比如基于 event loop 的 async/await),也可能切到别的线程(比如线程池调度)。协程本身不提供互斥、内存序或生命周期管理。
容易踩的坑:co_await 之后继续访问局部变量没问题,但访问堆对象、全局变量、this 指针所指内容时,必须确认它们的生命周期覆盖整个协程执行期。
- 不要在协程里
co_await一个正在析构的对象的 awaitable - lambda 捕获局部变量后生成协程,要注意该 lambda 是否逃逸出作用域
- 用
std::shared_ptr<t></t>管理跨co_await边界的对象,比裸指针靠谱得多
协程帧的分配方式(栈 or 堆)、销毁时机、异常传播路径,都依赖 promise_type 实现。标准库没提供开箱即用的通用协程类型,std::generator(C++23)只读、std::task 还没进标准 —— 得自己搭,或者用 libunifex、cppcoro 等第三方库。写错 promise_type 的 suspend logic,协程就卡死或崩溃,而且调试极难定位。








