在 C++20 中,协程(coroutines)被正式引入标准库,带来了更简洁、更安全的异步编程模型。与传统的线程或回调机制相比,协程允许我们在一个函数内部挂起和恢复执行,从而实现非阻塞的逻辑流。本文将从协程的基本原理、关键语法、实现原理以及典型使用场景四个方面,深入剖析 C++20 协程。
1. 协程的基本概念
协程是一种可挂起(suspend)和恢复(resume)的函数,它能够在执行过程中暂停自己的状态,等待某些事件或条件后再继续执行。与普通函数不同,协程的执行不必一次性完成,能在任意点暂停,极大地提升了代码的可读性和可维护性。
协程的核心特征有:
- 挂起点:通过
co_await、co_yield 或 co_return 等关键字实现。
- 状态保存:协程暂停时,所有本地变量状态会被保存到协程框架中。
- 恢复机制:外部通过
resume 或 operator bool 等方式触发协程恢复。
2. C++20 中的协程语法
| 关键字 |
用途 |
co_await |
等待一个 awaitable 对象(如 std::future 或自定义 awaitable) |
co_yield |
产生一个值给调用者(常用于生成器) |
co_return |
结束协程并返回结果 |
co_await + operator co_await |
定义自定义 awaitable 类型 |
2.1 协程函数定义
C++20 通过 co_return 或返回类型为 `std::generator
` / `std::task` 的函数来声明协程。例如:
“`cpp
std::generator
count_to(int n) {
for (int i = 0; i h) {
// 异步事件后恢复协程
std::async([h](){ h.resume(); });
}
int await_resume() { return 42; } // 恢复时返回值
};
“`
### 3. 协程的实现原理
在底层,协程由编译器生成一个状态机。每个挂起点对应一个 `state`,编译器会把函数体拆分成若干段,插入代码以保存和恢复本地变量。生成的状态机以 `std::coroutine_handle` 对象形式存在。
**关键步骤**:
1. **入口**:调用协程函数时,编译器创建协程框架(promise 对象)并返回 `coroutine_handle`。
2. **挂起**:到达 `co_await` 或 `co_yield` 时,`await_ready` 判断是否立即完成;若需挂起,则 `await_suspend` 被调用,传入当前协程句柄。
3. **恢复**:在 `await_suspend` 的异步操作完成后,调度器调用 `h.resume()`,使协程重新进入执行状态。
4. **结束**:执行到 `co_return` 或函数末尾时,调用 `promise_type::get_return_object` 返回最终结果。
编译器将所有这些逻辑封装在生成的状态机类中,开发者只需关注业务逻辑。
### 4. 典型使用场景
#### 4.1 异步 I/O
利用协程与 async I/O 框架(如 Boost.Asio、libuv)配合,能够写出类似同步代码的异步逻辑。
“`cpp
asio::awaitable
read_file(const std::string& path) {
asio::streambuf buffer;
std::ifstream file(path, std::ios::binary);
co_await asio::async_read(file, buffer, asio::use_awaitable);
// 处理读取的数据
}
“`
#### 4.2 生成器(惰性序列)
`std::generator
` 让我们可以轻松实现惰性序列,例如斐波那契数列:
“`cpp
std::generator
fibonacci(int n) {
int a = 0, b = 1;
for (int i = 0; i parallel_sum(const std::vector& data) {
return std::async(std::launch::async, [&]{
int sum = 0;
for (int x : data) sum += x;
return sum;
});
}
int main() {
auto fut1 = parallel_sum(vec1);
auto fut2 = parallel_sum(vec2);
co_await fut1; // 等待 fut1 完成
co_await fut2; // 等待 fut2 完成
}
“`
### 5. 开发者注意事项
1. **资源管理**:协程在挂起期间会占用堆内存,需注意内存泄漏。使用 `std::unique_ptr` 或 `std::shared_ptr` 结合 `awaitable` 的 `await_resume` 时,确保资源及时释放。
2. **错误传播**:协程可以抛出异常,异常会通过 `promise_type::unhandled_exception()` 传递给调用者,使用 `try-catch` 捕获即可。
3. **调试难度**:协程的执行路径不再是直线,调试时需要借助工具或日志辅助定位挂起点。
4. **性能开销**:协程的状态机实现虽然轻量,但不适合极高频率、毫秒级别的业务。对性能要求极高的场景仍可考虑传统线程或自研轻量级协程。
### 6. 结语
C++20 协程为语言注入了现代异步编程的便利性,使得写出可读性高、错误率低的异步代码变得可行。虽然协程技术尚在不断成熟,但它已经成为高性能服务器、游戏引擎、网络库等领域的核心技术之一。掌握协程的基本语法与使用模式,将为你的 C++ 开发旅程注入新的活力。
祝你在协程的海洋里畅游无阻!