C++20 在标准库中正式引入了协程(coroutine)概念,为异步编程和生成器模式提供了统一、类型安全且高效的实现手段。本文将从协程的工作机制、关键语言特性、编译器支持以及实际应用场景四个方面进行探讨,并给出一个简易的协程实现示例,帮助读者快速上手。
1. 协程的工作原理
协程本质上是一种能够在执行过程中暂停并恢复的函数。它与传统的函数不同之处在于,协程能够在任意位置 co_await、co_yield 或 co_return,并将执行状态(局部变量、程序计数器等)保存在协程句柄中。下图展示了协程在运行时的基本状态转换:
+-----------------+ +-----------------+ +-----------------+
| 准备阶段 | ---> | 运行中 | ---> | 等待/暂停 |
| 协程对象创建 | | 协程执行到 co_ | | 通过 co_await |
| 句柄绑定 | | await / yield | | 等待外部事件 |
+-----------------+ +-----------------+ +-----------------+
协程的核心是 协程句柄(std::coroutine_handle)和 协程 Promise(promise_type)。协程句柄是运行时的控制接口,负责挂起、恢复和销毁协程。Promise 对象存放协程执行期间产生的值、异常信息以及协程返回值。
2. 关键语言特性
| 关键字 | 作用 |
|---|---|
co_await |
暂停协程,等待一个 awaitable 对象完成后恢复执行 |
co_yield |
产生一个值并挂起协程,类似生成器的 yield |
co_return |
结束协程并返回最终结果 |
std::suspend_always / std::suspend_never |
控制协程在特定点是否挂起 |
operator co_await |
通过重载让自定义类型可被 co_await |
注意:
co_await的左侧对象必须满足Awaitable协议,至少需要实现await_ready(),await_suspend(),await_resume()三个成员函数。
3. 编译器与标准库支持
目前主流编译器(Clang 10+, GCC 10+, MSVC 19.29+)均已实现 C++20 协程特性。标准库中提供了 std::future, std::async 等异步接口的协程实现,并新增了 std::generator(C++23),以及 std::ranges::coroutine 的工具。
如果需要在项目中使用协程,建议:
- 开启 C++20 标准(如
-std=c++20)。 - 使用
-fcoroutines(GCC/Clang)或-experimental:coroutines(MSVC)。 - 链接
libc++(Clang)或stdc++(GCC)时,注意包含-pthread以避免多线程同步问题。
4. 一个简易协程实现示例
下面演示一个生成整数序列的协程,类似于 Python 的 range() 函数:
#include <coroutine>
#include <iostream>
#include <exception>
#include <memory>
template<typename T>
struct Generator {
struct promise_type;
using handle_type = std::coroutine_handle <promise_type>;
struct promise_type {
T value_;
std::exception_ptr eptr_;
Generator get_return_object() {
return Generator{handle_type::from_promise(*this)};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
std::suspend_always yield_value(T value) {
value_ = value;
return {};
}
void return_void() {}
void unhandled_exception() { eptr_ = std::current_exception(); }
};
handle_type coro_;
explicit Generator(handle_type h) : coro_(h) {}
~Generator() { if (coro_) coro_.destroy(); }
// 让外部使用迭代器风格遍历
struct iterator {
handle_type coro_;
bool done_ = false;
iterator(handle_type h, bool done) : coro_(h), done_(done) {}
iterator& operator++() {
coro_.resume();
if (coro_.done()) done_ = true;
return *this;
}
T operator*() const { return coro_.promise().value_; }
bool operator==(const iterator& other) const { return done_ == other.done_; }
bool operator!=(const iterator& other) const { return !(*this == other); }
};
iterator begin() {
if (!coro_.done()) coro_.resume();
return iterator{coro_, coro_.done()};
}
iterator end() { return iterator{coro_, true}; }
};
Generator <int> range(int start, int end, int step = 1) {
for (int i = start; i < end; i += step) {
co_yield i;
}
}
int main() {
for (int x : range(0, 10, 2)) {
std::cout << x << ' ';
}
std::cout << '\n';
return 0;
}
运行结果:
0 2 4 6 8
该示例展示了协程的基本构造:promise_type、co_yield、协程句柄与迭代器兼容。
5. 实际应用场景
-
异步 IO
结合co_await与网络库(如 Boost.Asio 或 libuv)实现事件驱动的服务器,代码更像同步流程,易于维护。 -
生成器
用于遍历大规模数据集(如数据库查询、文件流)时,避免一次性把所有数据读入内存,节省资源。 -
协程调度
在游戏引擎、GUI 框架中实现轻量级任务调度器,减少线程切换成本。 -
流水线式处理
将多个协程串联成处理链,每个阶段在co_yield处产生中间结果,构建可组合的处理管道。
6. 小结
C++20 协程为 C++ 提供了强大的异步编程模型,兼具性能与语义清晰。通过理解协程的内部机制、掌握关键语言特性,并结合标准库工具,开发者可以写出更简洁、易维护的异步代码。随着编译器成熟度的提升,协程将成为未来 C++ 开发的常见手段。