C++20 的协程(coroutines)是一项强大的语言特性,它使得异步编程、生成器、延迟求值等任务变得更直观、更易于维护。本文将从协程的基本概念讲起,逐步演示如何在现代 C++ 项目中使用协程,并给出几个常见的实战场景。
1. 协程的核心思想
协程本质上是一种可挂起和恢复的函数。与普通函数不同,协程在执行过程中可以被挂起(co_await、co_yield 或 co_return),随后在需要时继续执行。这样就可以把异步操作或耗时计算拆分成若干步骤,避免阻塞线程,同时保持代码的顺序性和可读性。
关键点:
- 挂起点:
co_await, co_yield, co_return。这些关键字会导致协程挂起或完成。
- 返回类型:协程的返回类型不是
void 或 T,而是一个 协程类型,例如 `std::future
`、`std::generator`、或自定义类型。
- promise:每个协程都有一个
promise 对象,负责管理协程的状态、结果以及异常传播。
2. 协程的典型实现:生成器(generator)
C++20 标准库尚未正式提供 std::generator,但可以通过第三方库(如 cppcoro)或自定义实现。下面给出一个简单的 `generator
` 实现,用于演示协程如何工作。
“`cpp
#include
#include
#include
#include
template
struct generator {
struct promise_type;
using handle_type = std::coroutine_handle
;
struct promise_type {
T current_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) {
current_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(); }
generator(const generator&) = delete;
generator& operator=(const generator&) = delete;
generator(generator&& other) noexcept : coro(other.coro) { other.coro = nullptr; }
generator& operator=(generator&& other) noexcept {
if (this != &other) {
if (coro) coro.destroy();
coro = other.coro;
other.coro = nullptr;
}
return *this;
}
bool move_next() {
if (!coro.done()) {
coro.resume();
if (coro.promise().eptr) std::rethrow_exception(coro.promise().eptr);
}
return !coro.done();
}
T current_value() const { return coro.promise().current_value; }
};
“`
使用示例:
“`cpp
generator
count_to_n(int n) {
for (int i = 1; i >
std::future
read_file(const std::string& path) {
auto data = co_await async_read(path);
process_data(data);
}
“`
### 3.2 事件循环
在游戏或 GUI 程序中,事件循环经常需要处理多路 I/O 与定时器。使用协程可以把每个事件处理器写成一个协程,事件触发时 `resume` 相应协程即可。
### 3.3 生成器与流式计算
生成器非常适合用于流式数据处理,例如大文件的逐行读取、数据流水线。通过 `co_yield` 逐个输出值,消费者可以按需获取,避免一次性加载全部数据。
## 4. 性能与注意事项
– **协程开销**:协程的栈由编译器在堆上分配,开销低于传统线程,但不宜在大量频繁调用的小任务中使用。
– **异常传播**:协程内抛出的异常会被存储在 `promise`,在 `resume` 时再次抛出,需捕获或处理。
– **协程类型**:标准库不直接提供 `generator`,但可以自定义或使用第三方库;`std::future`、`std::shared_future` 等也可用作协程返回类型。
## 5. 小结
C++20 的协程为现代 C++ 带来了更强大的异步编程能力。理解其挂起、promise 与返回类型的机制,可以让你在项目中更加灵活地处理异步任务、生成序列或实现复杂的控制流。随着编译器与标准库的持续完善,协程将在更广泛的场景中得到应用。祝你在使用协程时玩得开心、代码更优雅!