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

协程(Coroutine)是 C++20 中引入的一项强大特性,它允许我们在不使用线程的情况下实现异步、非阻塞的代码。协程通过“挂起”和“恢复”机制,将函数的执行状态保存起来,后续可以在需要时继续执行,从而实现更高效的 IO、网络和生成器等功能。本文将从协程的概念、核心语法、实现细节以及一个完整的示例,帮助你快速上手 C++20 协程。


1. 协程的基本概念

  • 挂起(Suspend):协程在执行到 co_awaitco_yieldco_return 时会暂停,保存当前上下文(寄存器、栈帧、局部变量等)。
  • 恢复(Resume):调用协程对象的 resume()(或通过 co_await 调用)时,协程从上一次挂起的点继续执行。
  • 协程句柄(Coroutine Handle)std::coroutine_handle<> 对象负责管理协程的生命周期,提供 resume()destroy() 等操作。

协程本质上是一种轻量级的“线程”,它们不需要操作系统调度,所有上下文切换由编译器生成的状态机完成,开销更低。


2. 关键语法和类型

关键字 说明
co_await 等待一个 awaitable 对象,挂起协程直到 awaitable 完成。
co_yield 产生一个值,挂起协程,等待下一次 resume
co_return 结束协程,返回值给调用方。
std::suspend_always / std::suspend_never 决定协程是否在进入时立即挂起。

协程函数的返回类型必须是 `std::future

`、`std::generator`(C++23 提供)或者自定义的 `promise_type`。最常见的是: “`cpp std::future async_add(int a, int b); “` 此函数在内部生成一个 `promise_type`,该类型定义协程的行为(挂起、返回值等)。 — ### 3. Promise Type 的实现 `promise_type` 是协程的核心,负责: 1. **创建协程句柄** 2. **定义挂起行为**:通过 `initial_suspend()` 与 `final_suspend()` 决定协程是否立即挂起。 3. **获取返回值**:`get_return_object()` 返回给调用方的对象。 4. **错误处理**:`unhandled_exception()` 捕获异常。 下面给出一个最小可运行的 `promise_type` 示例,返回 `int`: “`cpp struct simple_promise { int value_; std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } int get_return_object() { return value_; } void return_value(int v) { value_ = v; } void unhandled_exception() { std::terminate(); } }; “` 使用 `simple_promise` 的协程函数: “`cpp simple_promise async_add(int a, int b) { co_return a + b; } “` — ### 4. 一个完整的异步 IO 示例 下面演示如何使用协程实现一个简单的异步 TCP 客户端,利用 `asio` 库的协程接口: “`cpp #include #include #include #include #include using asio::ip::tcp; using namespace std::chrono_literals; struct asio_promise { std::string result_; std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } std::string get_return_object() { return result_; } void return_value(std::string v) { result_ = std::move(v); } void unhandled_exception() { std::terminate(); } }; asio_promise async_read(tcp::socket& socket) { char data[1024]; std::size_t n = co_await asio::async_read(socket, asio::buffer(data), asio::transfer_at_least(1), asio::use_awaitable); co_return std::string(data, n); } int main() { asio::io_context io; tcp::resolver resolver(io); auto endpoints = resolver.resolve(“example.com”, “http”); tcp::socket socket(io); asio::async_connect(socket, endpoints, asio::use_awaitable); std::string request = “GET / HTTP/1.1\r\nHost: example.com\r\n\r\n”; co_await asio::async_write(socket, asio::buffer(request), asio::use_awaitable); std::string response = co_await async_read(socket); std::cout

发表评论