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

C++20引入了协程(Coroutines)这一强大特性,它为异步编程提供了一种更直观、可维护的语法。本文从协程的基本概念讲起,逐步引入关键类型与语法,并通过一个简易的异步网络请求示例,展示协程在实际项目中的应用。
一、协程概念回顾
协程本质上是一种“挂起”与“恢复”函数执行的机制。与传统线程不同,协程的上下文切换几乎不涉及系统调用,主要由编译器生成的状态机实现。协程可以暂停执行(co_awaitco_yieldco_return),在需要时恢复,因而天然适合处理 I/O、事件驱动等延迟操作。
二、核心类型与语法

  1. std::coroutine_handle – 表示协程的句柄,允许外部控制协程生命周期。
  2. std::promise_type – 每个协程都有一个与之关联的 promise 类型,用于返回值或异常。
  3. co_await – 等待一个 awaitable 对象,协程会挂起直到该 awaitable 变为可完成。
  4. co_yield – 产生一个值并挂起协程,常用于生成器模式。
  5. co_return – 结束协程并返回结果。
    三、实现一个简单的 async 网络请求
    下面的示例演示如何使用协程包装一个异步 DNS 查询与 TCP 连接,最终返回响应。为了简化,使用了 asio(Boost.Asio 或 standalone Asio)作为底层 I/O 库。
    
    #include <asio.hpp>
    #include <coroutine>
    #include <iostream>

using asio::ip::tcp;

// awaitable 类型封装 template struct async_result { struct promise_type { T value; async_result get_return_object() { return async_result{std::coroutine_handle

::from_promise(*this)}; } std::suspend_always initial_suspend() noexcept { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void return_value(T v) noexcept { value = std::move(v); } void unhandled_exception() { std::exit(1); } }; std::coroutine_handle coro; T get() { return coro.promise().value; } }; async_result async_get_response(const std::string& host, const std::string& path) { asio::io_context ctx; // 1. DNS 查询 tcp::resolver resolver(ctx); auto results = co_await resolver.async_resolve(host, “http”, asio::use_awaitable); // 2. 建立 TCP 连接 tcp::socket socket(ctx); co_await asio::async_connect(socket, results, asio::use_awaitable); // 3. 发送 HTTP 请求 std::string request = “GET ” + path + ” HTTP/1.1\r\nHost: ” + host + “\r\nConnection: close\r\n\r\n”; co_await asio::async_write(socket, asio::buffer(request), asio::use_awaitable); // 4. 接收响应 std::string response; asio::streambuf buffer; co_await asio::async_read_until(socket, buffer, “\r\n”, asio::use_awaitable); std::istream stream(&buffer); std::getline(stream, response); // 读取状态行 // 这里略去解析头部、读取正文的细节 co_return response; } int main() { auto fut = async_get_response(“example.com”, “/”); std::string res = fut.get(); // 协程在此挂起,直至响应完成 std::cout << "Response: " << res << std::endl; return 0; } “` **四、协程的优势与局限** *优势* – **代码可读性高**:异步流程像同步写法,易于理解。 – **性能优越**:无线程切换开销,协程上下文切换由编译器控制。 – **资源占用低**:协程栈可按需分配,避免了线程栈 1MB 的固定消耗。 *局限* – **库支持不完整**:目前仅部分 I/O 库实现了 `use_awaitable`。 – **编译器兼容性**:需要 C++20 支持且编译器实现成熟。 – **错误传播**:异常传递与 `try/catch` 需要仔细设计。 **五、总结** C++20 的协程为复杂的异步逻辑提供了一种更清晰的表达方式。通过结合 Asio 等成熟库,你可以轻松构建高性能、低延迟的网络服务。随着生态逐步完善,协程有望成为 C++ 现代异步编程的核心工具。

发表评论