c++20协程核心是设计可挂起的异步操作流程,提供函数暂停/恢复机制,不自动并发,需手动调度;依赖promise_type、co_await、co_yield三组件,适用于异步I/O、生成器等场景。

用 C++20 协程,核心不是“写协程”,而是“设计可挂起的异步操作流程”——它不自动并发,也不替代线程,而是提供一种 函数可暂停/恢复 的底层机制,让你能自然表达异步逻辑(比如网络请求、状态机、生成器),同时把调度权交给你自己。
一、先搞懂三个关键组件:promise_type、co_await、co_yield
C++20 协程不是新语法糖,而是一套编译器支持的协作式机制,依赖三部分配合:
- promise_type:每个协程对象内部隐式关联一个 promise 对象,负责管理协程生命周期、决定如何挂起/恢复、返回什么给调用方。你必须在协程返回类型中嵌套定义它(或使用已有实现如
std::generator、task<t></t>)。 - co_await:让当前协程在某个“可等待对象”(awaiter)上挂起。挂起前调用
await_ready()判断是否需真挂起;若需,则调用await_suspend()注册恢复逻辑(比如投递到线程池);恢复时执行await_resume()返回值。 - co_yield:专用于生成器(generator)场景,等价于
co_await promise.yield_value(value),把值“产出”并挂起,下次恢复时继续往后走。
二、从一个最简 task 示例开始(无第三方库)
下面是一个手动实现的轻量 task<int></int>,支持 co_await 和返回值:
struct task { struct promise_type { int value_; task get_return_object() { return {}; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void return_value(int v) { value_ = v; } void unhandled_exception() { std::terminate(); } }; };
然后这样用:
立即学习“C++免费学习笔记(深入)”;
task my_coro() { co_await std::suspend_always{}; // 挂起一次 co_return 42; // 设置 promise.value_ 并结束 }
⚠️注意:这只是一个骨架。真正可用的 task 还需支持 co_await 其他 task、异常传播、内存分配控制(比如用 operator new 分配协程帧)。建议初期直接用 cppcoro 或 Folly 的现成 task 类型。
三、典型应用场景怎么写
① 异步 I/O 封装(如读文件)
不直接 await 系统调用,而是封装为 awaiter:
- 定义一个
async_read_file函数,返回自定义 awaitable 对象; - 其
await_suspend把回调注册到 io_uring 或 epoll; - 就绪后调用
resume()恢复协程,await_resume()返回读到的数据。
② 生成器(generator)
C++23 标准已带 std::generator<t></t>,C++20 可用 cppcoro 的 generator<t></t>:
cppcoro::generator<int> fib() { int a = 0, b = 1; while (true) { co_yield a; auto next = a + b; a = b; b = next; } } // 使用: for (int x : fib()) { if (x > 100) break; std::cout << x << " "; }
③ 状态机 / 游戏逻辑
把每帧更新、等待动画完成、条件分支等写成协程,比手写 state enum + switch 清晰得多:
task animate_sprite() { sprite.set_state("walk"); co_await delay_ms(2000); // 挂起2秒 sprite.set_state("jump"); co_await wait_for_animation("jump"); // 等待动画事件 co_return; }
四、避坑提醒:协程不是银弹
✘ 不等于多线程:协程默认在同一线程串行调度,要并发得自己配线程池或 Event loop。
✘ 不自动管理内存:协程帧(保存局部变量+状态)默认堆分配,忘了 delete 或没正确处理异常会导致泄漏。
✘ 调试困难:栈是碎片化的,gdb/lldb 对协程支持有限,建议搭配日志或专用调试工具(如 VS2022 的协程可视化)。
✘ 编译器支持需开启:Clang 13+/GCC 10+/MSVC 2019 16.11+,且需加 -std=c++20 -fcoroutines(GCC/Clang)或 /std:c++20 /await(MSVC)。
基本上就这些。协程的价值不在“炫技”,而在把嵌套回调、状态变量、中断恢复这些琐碎逻辑,重新拉回直觉化的顺序代码流里。入门建议从 cppcoro::generator 和 cppcoro::task 开始写几个小例子,跑通再深挖 promise 细节。