C++20 协程(Coroutine)的实现与应用

在 C++20 标准中,协程(coroutine)被正式纳入语言特性,为异步编程提供了更直观、更高效的实现方式。相比传统的回调、事件循环或者使用外部库(如 Boost.Asio)实现异步逻辑,协程让代码更接近同步写法,降低了复杂度并提升了可读性。下面我们从协程的底层实现机制、关键语法、标准库支持以及实际应用场景几个角度,对 C++20 协程进行全面剖析。

1. 协程的基本概念

协程是一种轻量级的函数,它能够在执行过程中暂停(co_await/co_yield/co_return),并在后续某个时刻恢复执行。与传统函数不同,协程的状态(局部变量、调用栈、程序计数器等)会被保存在堆上,以便在恢复时继续执行。

协程的三个核心关键词:

  • co_await:等待一个 awaitable 对象完成,暂停协程。
  • co_yield:向调用方产出一个值,暂停协程。
  • co_return:结束协程,返回结果。

2. 底层实现机制

2.1 生成器类型 std::generator

C++20 提供了 std::generator,它是一个协程的包装器,用于实现生成器模式(类似 Python 的 yield)。其基本使用方式:

std::generator <int> counter() {
    for (int i = 0; i < 10; ++i) {
        co_yield i;   // 产生一个值并暂停
    }
}

调用方可以通过标准容器或范围语法迭代:

for (int n : counter()) {
    std::cout << n << ' ';
}

2.2 Awaitable 对象

任何可以被 co_await 的对象都必须满足 Awaitable 的概念。最常用的标准 Awaitable 是 `std::future

`,但也可以自定义,例如: “`cpp struct SimpleAwait { bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle h) noexcept { // 在此调度协程恢复 std::thread([h](){ std::this_thread::sleep_for(std::chrono::seconds(1)); h.resume(); }).detach(); } void await_resume() const noexcept {} }; async_task my_task() { co_await SimpleAwait(); // 1 秒后恢复 } “` ### 2.3 Coroutine Handle 与 Promise 协程函数在编译时会生成一个 `promise_type`,负责管理协程状态。协程入口返回一个 `std::coroutine_handle `,调用 `resume()` 可以让协程继续执行。`co_return` 触发 `promise_type::return_value`,随后协程结束。 ### 2.4 内存分配 协程的状态(promise 对象、局部变量等)会被分配在堆上。使用 `std::allocator` 或者自定义分配器可以控制协程帧的分配策略。若不显式释放,协程会在结束时自动析构。 ## 3. 标准库支持 ### 3.1 `std::future` 与 `std::async` `std::future` 在 C++11/14/17 已经存在,但在 C++20 的协程中,`co_await std::future ` 变得更直观。你可以用 `co_await` 直接等待一个 `std::future`,不再需要 `get()` 或 `wait()`: “`cpp async_task get_value_async() { std::future fut = std::async(std::launch::async, []{ return 42; }); int val = co_await fut; co_return val; } “` ### 3.2 `std::task` / `std::generator` 标准库尚未为 `async_task` 提供完整实现,但常见做法是使用自定义模板: “`cpp template struct async_task { struct promise_type; using handle_type = std::coroutine_handle ; handle_type coro; async_task(handle_type h) : coro(h) {} ~async_task() { if (coro) coro.destroy(); } async_task(async_task&& other) noexcept : coro(other.coro) { other.coro = nullptr; } T get() { coro.resume(); return coro.promise().value; } }; “` ## 4. 实际应用案例 ### 4.1 异步文件读取 “`cpp async_task> read_file_async(const std::string& path) { std::ifstream file(path, std::ios::binary); std::vector data((std::istreambuf_iterator(file)), std::istreambuf_iterator ()); co_return data; } “` 这里 `co_return` 将文件内容打包为 `std::vector `,在调用方可通过 `get()` 或 `co_await` 获得。 ### 4.2 事件驱动网络编程 使用协程结合事件循环(如 libuv、IOCP 或 epoll): “`cpp async_task echo_server() { // 创建监听套接字 int sock = socket(AF_INET, SOCK_STREAM, 0); // 绑定、监听 // … for (;;) { co_await async_accept(sock); // 等待连接 // 为每个连接创建协程 spawn(async_echo_handler(client_sock)); } } “` 协程的暂停/恢复完美配合事件通知,避免了大量回调层。 ### 4.3 并行任务调度 协程天然适合实现任务池: “`cpp std::vector> workers; for (int i = 0; i

发表评论