C++20协程:从概念到实践

C++20 的协程(coroutines)是一项强大的语言特性,它使得异步编程、生成器、延迟求值等任务变得更直观、更易于维护。本文将从协程的基本概念讲起,逐步演示如何在现代 C++ 项目中使用协程,并给出几个常见的实战场景。

1. 协程的核心思想

协程本质上是一种可挂起和恢复的函数。与普通函数不同,协程在执行过程中可以被挂起(co_awaitco_yieldco_return),随后在需要时继续执行。这样就可以把异步操作或耗时计算拆分成若干步骤,避免阻塞线程,同时保持代码的顺序性和可读性。

关键点:

  • 挂起点co_await, co_yield, co_return。这些关键字会导致协程挂起或完成。
  • 返回类型:协程的返回类型不是 voidT,而是一个 协程类型,例如 `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 与返回类型的机制,可以让你在项目中更加灵活地处理异步任务、生成序列或实现复杂的控制流。随着编译器与标准库的持续完善,协程将在更广泛的场景中得到应用。祝你在使用协程时玩得开心、代码更优雅!

发表评论