协程(Coroutines)是 C++20 标准中引入的一项功能,旨在为异步编程提供一种更直观、更轻量的实现方式。相比传统的基于回调、事件循环或多线程的异步模型,协程通过语法糖隐藏了状态机的细节,使得代码更加简洁、易读。本文将从协程的基本概念、关键字以及典型使用场景展开,帮助你快速上手并在实际项目中灵活运用。
一、协程的核心概念
-
协程函数
协程函数是使用co_await,co_yield或co_return的普通函数。它们的返回类型必须是std::future,std::generator或std::task(自定义实现)等支持协程的类型。 -
挂起与恢复
当协程遇到co_await时会挂起,执行权交还给调用者;当被等待的对象完成后,协程继续执行。类似地,co_yield会将值返回给调用者,协程暂停。 -
状态机自动生成
编译器会把协程展开成一个内部状态机类,隐藏所有挂起点与恢复点。程序员只需关注业务逻辑即可。
二、关键字解析
| 关键字 | 作用 | 示例 |
|---|---|---|
co_await |
挂起协程,等待表达式完成 | int result = co_await async_operation(); |
co_yield |
生成一个值并挂起协程 | co_yield value; |
co_return |
结束协程并返回结果 | co_return final_value; |
co_spawn(非标准) |
启动协程 | co_spawn(async_io_service, my_coroutine()); |
注意:
co_spawn不是标准库的一部分,常见于 Boost.Asio 或者 libco 等库。
三、典型使用场景
-
异步 I/O
在网络编程或文件读写中,协程可以直接等待 I/O 完成,而不必显式管理事件循环。std::future<std::string> fetch_url(const std::string& url) { auto sock = tcp::socket(io_context); co_await sock.async_connect(url, boost::asio::use_future); std::vector <char> buffer(1024); size_t n = co_await sock.async_read_some(boost::asio::buffer(buffer), boost::asio::use_future); co_return std::string(buffer.begin(), buffer.begin() + n); } -
流水线处理
使用co_yield构造生产者-消费者管道,减少临时容器与复制开销。std::generator <int> fibonacci(int n) { int a = 0, b = 1; for (int i = 0; i < n; ++i) { co_yield a; int tmp = a + b; a = b; b = tmp; } } -
协程化的状态机
用协程实现复杂状态机,状态转移自然对应协程的挂起点。std::future <void> traffic_light() { while (true) { co_await std::chrono::seconds(10); // 红灯 std::cout << "Red\n"; co_await std::chrono::seconds(5); // 绿灯 std::cout << "Green\n"; } }
四、性能与实现细节
-
栈浅拷贝
协程内部状态机由编译器生成,默认在栈上分配,避免了 heap 分配的成本。 -
异常传播
如果协程内部抛出异常,co_return会将其包装为std::future::exception_ptr,调用者可以通过future.get()捕获。 -
内存占用
协程的对象大小等于其捕获变量和状态机指针。对于大型协程,使用std::move或std::unique_ptr传递捕获对象可降低复制开销。
五、常见坑与最佳实践
-
避免过度挂起
每次co_await都涉及上下文切换,过度使用会导致性能下降。建议只在真正需要等待的地方使用。 -
统一执行上下文
协程往往需要在同一io_context或线程池中执行,避免跨线程挂起导致同步问题。 -
错误处理
通过try...catch包裹协程主体,或者在std::future上调用wait()后检查exception_ptr,确保异常被正确捕获。
六、总结
C++20 的协程为开发者提供了一种更自然、更高效的异步编程方式。它把异步逻辑写成同步样式,极大提升代码可读性与维护性。随着标准库和第三方库对协程支持的完善,未来我们将看到越来越多基于协程的高性能网络框架、数据库驱动以及并行计算库。掌握协程,将为你的 C++ 技能树添上一颗璀璨的新星。