c++20的协程(Coroutines)是什么 编写高效的异步代码【详解】

C++20协程是可挂起/恢复的语言级函数机制,非线程亦非普通函数调用;其本质是将执行状态保存至堆上coroutine frame,依赖promise、awaiter和handle三组件,需配合executor实现高效异步调度。

C++20 协程不是线程,也不是简单的函数调用替换,而是一种语言级的、可挂起和恢复的函数机制,用于编写逻辑清晰、资源可控的异步代码。它不依赖操作系统调度,不自动并发,但为 async/await 风格的异步编程提供了底层支撑——真正高效的关键,在于避免回调地狱、减少栈拷贝、复用内存,并与 executor(执行器)协同调度。

协程的本质:可暂停的函数

普通函数执行完就销毁栈帧;协程函数(用 co_awaitco_yieldco_return 标记)在遇到挂起点时,会把当前执行状态(局部变量、PC 位置等)保存到堆上的一块内存(即 coroutine frame),然后返回控制权。之后可在任意时机恢复执行,从上次暂停处继续。

  • 协程函数返回类型必须满足 std::coroutine_traits 要求,通常自定义一个 TaskGeneratorFuture 类型
  • 编译器自动生成三个关键组件:promise 对象(管理生命周期和返回值)、awaiter(决定何时挂起/恢复)、coroutine handle(用于手动调度或传递)
  • 没有运行时调度器 —— co_await 后是否真“等待”,取决于你提供的 awaiter 的 await_ready()await_suspend() 实现

如何写出高效的异步协程代码

高效 ≠ 写得短,而是减少分配、避免隐式拷贝、明确调度意图、适配执行上下文。

  • 复用协程帧内存:默认 new/delete 分配帧,高频协程开销大。可通过 promise 类型重载 operator new/operator delete 使用对象池或栈内存(如 std::array + placement new)
  • 避免值拷贝:传参尽量用 const lvalue 引用或 move;返回 large object 前考虑用 co_yield 流式产出,或让 promise 存储指针而非副本
  • co_await 表达等待语义,而非逻辑分支:比如不要写 if (ready()) co_await something();,而是让 awaiter 的 await_ready() 返回 true/false,由编译器决定跳过挂起
  • 绑定执行器(executor):在 await_suspend() 中把 handle 提交给线程池、IO 多路复用器(如 epoll/kqueue 封装)或特定线程,实现真正的异步调度

一个轻量级 awaiter 示例(无栈 IO 等待)

假设你封装了一个基于 epollasync_read 操作:

struct epoll_awaiter {
  https://www./link/d3e4b9f9aa7aac3bd930abb82fab2d2b fd_;
  std::span buf_;
  bool ready_ = false;

bool awaitready() { return ready; }

void await_suspend(std::coroutine_handle<> h) { // 注册 fd 到 epoll,就绪后调用 h.resume() register_forread(fd, h { h.resume(); }); }

void await_resume() {} // 无需返回值 };

这样,co_await epoll_awaiter{fd, buf} 就是零拷贝、无栈挂起、事件驱动的等待 —— 不占线程、不阻塞、不轮询。

常见误区与提醒

  • 协程本身不提供并发:多个协程可在同一线程串行调度,也可分发到多线程,取决于你的 executor 设计
  • 不要在协程中裸写 sleep(1)std::this_thread::sleep_for:这会阻塞整个执行器线程。应使用基于 timerfd 或 event loop 的 awaitable sleep
  • 异常传播需显式处理:promise 的 unhandled_exception() 必须定义,否则程序 terminate
  • 移动语义要小心:coroutine handle 是可移动不可复制的,跨 await 边界传递时注意所有权

协程的价值不在语法炫技,而在把异步控制流还原为接近同步的书写体验,同时保留底层掌控力。写得好,它比 callback 更省内存、比 thread 更低开销、比 future 更易组合。