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

在 C++20 标准中,协程(coroutines)被正式纳入标准库,提供了一套统一、轻量级的异步编程模型。与传统的多线程编程相比,协程在性能、可读性和资源管理方面都有显著优势。本文将从协程的基本概念、实现机制、与标准库的协作,以及实战案例等几个角度,对 C++ 协程进行系统梳理。

1. 协程的基本概念

协程是一种在单线程环境下可以挂起和恢复执行的函数。与线程不同,协程的挂起点是由程序员显式控制,协程可以在需要的时候主动让出控制权,随后在需要时恢复执行。协程的核心特点包括:

  • 非抢占式切换:协程切换是通过 co_yieldco_awaitco_return 等关键字实现的,控制权的转移完全由协程内部决定。
  • 轻量级:协程的栈空间与线程相比要小得多,甚至可以在堆中动态分配。
  • 无共享状态:协程内部的局部变量默认保持独立,避免了多线程并发访问导致的数据竞争。

2. 协程的实现机制

在 C++20 里,协程由三大组件协同完成:

  1. 协程句柄(coroutine handle)

    • 通过 std::coroutine_handle<> 类型获取,表示对协程的控制权。
    • 句柄提供 resume()destroy()done() 等成员函数。
  2. 悬停点(suspend point)

    • 通过 co_awaitco_yieldco_return 等关键字产生。
    • 每个悬停点对应一个 awaitable 对象,该对象需要实现 await_ready()await_suspend()await_resume() 三个方法。
  3. 协程 Promise

    • 协程函数的返回类型为 `std::future ` 或 `std::generator` 时,编译器会生成对应的 Promise 对象。
    • Promise 用来收集协程执行过程中的信息,如返回值、异常、等待对象等。

协程的编译器后端通过将协程函数拆分为若干个状态机步骤,将 co_await 等关键字插入状态机中,从而实现挂起与恢复。

3. 协程与 std::future、std::promise 的区别

  • std::future / std::promise 依赖线程池或后台线程来完成异步操作,存在线程上下文切换成本。
  • 协程是同步语义的异步实现,调用者可以像写同步代码一样书写异步流程,编译器负责隐藏状态机细节。

4. C++ 协程的标准库支持

4.1 std::generator

`std::generator

` 是对协程的一种封装,用于产生一系列值。典型的使用场景包括: “`cpp std::generator range(int start, int end) { for (int i = start; i async_task() { return std::async(std::launch::async, []{ return 42; }); } std::future get_value() { std::future f = async_task(); int val = co_await std::experimental::make_ready_future(f.get()); // 等待 co_return val; } “` ### 4.3 std::experimental::awaitable 实验性标准库提供了一套 `awaitable` 接口,用于自定义协程的等待对象。通过实现 `await_ready`、`await_suspend`、`await_resume` 三个方法,用户可以让任何对象变成可 `co_await` 的。 ## 5. 协程实战:异步网络请求 下面给出一个简化的异步 HTTP 客户端示例,使用 Boost.Asio 的协程接口(`boost::asio::awaitable`)实现: “`cpp #include #include #include #include #include using boost::asio::ip::tcp; using namespace boost::asio::ip; // 简化的异步 GET 请求 boost::asio::awaitable async_get(tcp::resolver& resolver, const std::string& host, const std::string& target) { auto executor = co_await boost::asio::this_coro::executor; // 解析地址 auto endpoints = co_await resolver.async_resolve(host, “http”, boost::asio::use_awaitable); // 建立连接 tcp::socket socket(executor); co_await boost::asio::async_connect(socket, endpoints, boost::asio::use_awaitable); // 发送请求 std::string request = “GET ” + target + ” HTTP/1.1\r\n” “Host: ” + host + “\r\n” “Connection: close\r\n\r\n”; co_await boost::asio::async_write(socket, boost::asio::buffer(request), boost::asio::use_awaitable); // 接收响应 boost::asio::streambuf response; co_await boost::asio::async_read_until(socket, response, “\r\n”, boost::asio::use_awaitable); std::istream resp_stream(&response); std::string http_version; unsigned int status_code; std::string status_message; resp_stream >> http_version >> status_code >> status_message; std::cout

发表评论