在C++20中,协程(Coroutines)被正式加入语言核心,为异步编程带来了革命性的改进。相比传统的回调链、事件循环或基于线程的并发模型,协程以轻量、易读的方式隐藏了复杂的状态机,让开发者可以像写顺序代码那样书写异步逻辑。本文将从协程的基本概念、关键语法、实现原理以及常见应用场景入手,帮助你快速掌握C++20协程。
1. 协程到底是什么?
协程是可以在执行过程中挂起并在未来某个时间点恢复的函数。挂起点通过co_await、co_yield或co_return语句实现。协程内部维护的状态机由编译器自动生成,外部开发者只需要关注业务逻辑。
关键点
- 挂起(Suspend):
co_await、co_yield- 恢复(Resume):协程对象被再次调用或异步事件触发
- 完成(Completion):
co_return或异常抛出
2. 语法要点
// 协程返回类型必须是 std::future <T>、std::generator<T> 或自定义 Awaitable
std::future <int> asyncAdd(int a, int b) {
// 这里可以先做同步工作
std::this_thread::sleep_for(std::chrono::milliseconds(100));
co_return a + b; // 结束协程,返回结果
}
std::generator <int> range(int n) {
for (int i = 0; i < n; ++i) {
co_yield i; // 挂起,返回一个值
}
}
2.1 Awaitable对象
协程只能挂起与“可等待”对象。Awaitable必须实现:
bool await_ready() const noexcept; // 是否立即完成
void await_suspend(std::coroutine_handle<> h); // 挂起时执行
await_resume() const noexcept; // 恢复时返回值
标准库提供的std::future、std::promise、std::generator已实现这些接口。
2.2 协程句柄
std::coroutine_handle<>是协程的入口点。通过句柄可以:
- 调用
resume()恢复协程 - 检查
done()是否结束 - 调用
destroy()销毁协程
3. 编译器生成的状态机
协程本质上是一个隐式生成的状态机。编译器会:
- 为每个挂起点生成一个状态编号。
- 把局部变量放入生成器的“悬挂”结构(heap allocation)。
- 将
co_await、co_yield转换为调用await_suspend()、await_resume()。 - 生成
resume()函数,按状态执行对应代码块。
这意味着协程比普通函数更消耗内存(需要堆分配),但挂起与恢复的成本远低于线程切换。
4. 常见应用
4.1 简化网络 I/O
std::future<std::string> fetch(const std::string& url) {
// 假设socket是 Awaitable
auto socket = std::make_shared <AsyncSocket>(url);
co_await socket->connect();
std::string data;
while (true) {
std::string chunk = co_await socket->read(); // 挂起等待数据
if (chunk.empty()) break; // EOF
data += chunk;
}
co_return data;
}
无需回调链,错误处理也可用try/catch。
4.2 生成异步迭代器
利用std::generator可以像同步迭代器一样遍历异步结果。
for (int x : asyncRange(10)) { // asyncRange 返回 std::generator <int>
std::cout << x << '\n';
}
4.3 任务调度器
将std::future与协程结合,可实现轻量级的协程调度器。任务以co_await方式挂起,调度器在事件到来时恢复。
5. 性能与注意事项
- 内存占用:协程会在堆上分配悬挂结构,若协程频繁创建需注意泄漏与内存碎片。
- 异常安全:异常在协程内部会自动传播到
co_return点,调用者可捕获。 - 调试难度:协程代码的控制流被拆分,调试时需要使用IDE的协程支持(如Visual Studio、CLion)。
6. 小结
C++20协程提供了语法糖与编译器自动生成的状态机,让异步编程变得既直观又高效。通过std::future、std::generator以及自定义Awaitable,开发者可以轻松实现非阻塞I/O、流式数据处理、协作式多任务等功能。掌握协程的核心概念和语法后,你将能够将传统繁琐的异步代码转化为简洁、可维护的顺序式代码,极大提升开发效率与代码质量。