在 C++20 中,协程(coroutines)作为一种强大的异步编程工具正式被标准化。相比传统的线程或回调,协程在语义清晰、性能开销小、代码可读性高方面具有明显优势。本文将系统梳理协程的核心概念、实现细节,并给出一个完整的网络请求示例,帮助读者快速上手。
1. 协程的基本语法
协程函数使用 co_await、co_yield 和 co_return 关键字实现:
co_return; // 结束协程,返回值可用
co_yield x; // 暂停并返回一个值,后续可继续
co_await expr; // 暂停等待 expr 的结果
协程函数必须返回 std::experimental::coroutine_handle 或者相关的协程返回类型。C++20 标准库提供了 std::generator(实验性)来简化生成器的实现。
2. 协程的执行模型
协程的执行模型分为两部分:
| 阶段 | 说明 |
|---|---|
| 生成 | 在调用协程函数时,编译器生成一个 悬挂对象,即 std::coroutine_handle<>。此时并未真正执行函数体。 |
| 恢复 | 当外部调用 handle.resume() 或者使用 co_yield、co_await 触发时,协程恢复执行,直至遇到下一个暂停点或结束。 |
协程的暂停点(co_await、co_yield、co_return)会保存当前执行状态(局部变量、指令指针等),便于后续恢复。
3. 关键概念拆解
-
Promise
协程返回类型中会包含一个 promise 对象,负责定义协程的行为(如异常处理、返回值类型等)。Promise 的生命周期与协程句柄绑定,协程结束后自动销毁。 -
Awaitable
co_await后面可以跟任意 awaitable 对象。编译器会在后台调用await_ready()、await_suspend()和await_resume()三个成员函数来决定协程是否立即返回、挂起或获取结果。 -
Awaiter
一个 awaitable 对象会提供 awaiter,负责实际的挂起/恢复逻辑。标准库中常见的 awaiter 如std::suspend_always、std::suspend_never。
4. 常见协程返回类型
- `std::generator `(实验性):用于生成器,支持 `co_yield`。
- `std::future `:与 `std::async` 相似,支持 `co_await`。
- 自定义 Promise:可实现更复杂的协程行为,如异步 IO、任务调度器等。
5. 实战示例:异步 HTTP GET 请求
下面给出一个使用 Boost.Asio + C++20 协程的异步 HTTP GET 示例,演示如何在协程中等待网络 IO。
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <iostream>
#include <string>
#include <coroutine>
#include <future>
namespace asio = boost::asio;
namespace beast = boost::beast;
namespace http = beast::http;
// 简化的 Awaitable:等待异步操作完成
template<class AsyncOperation>
struct awaitable
{
AsyncOperation op;
asio::yield_context yield;
std::error_code ec;
awaitable(AsyncOperation&& op, asio::yield_context yield)
: op(std::move(op)), yield(yield) {}
bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<> h) { op(yield); }
void await_resume() const noexcept { if (ec) throw std::system_error(ec); }
};
struct http_get
{
std::string host, target;
unsigned short port;
asio::io_context& ioc;
http_get(std::string host, std::string target, unsigned short port,
asio::io_context& ioc)
: host(std::move(host)), target(std::move(target)), port(port), ioc(ioc) {}
std::future<std::string> operator()()
{
struct promise_type {
std::promise<std::string> prom;
http_get* self;
auto get_return_object() { return std::future<std::string>(prom.get_future()); }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_value(std::string&& val) { prom.set_value(std::move(val)); }
void unhandled_exception() { prom.set_exception(std::current_exception()); }
};
return co_spawn(ioc, [this]() -> std::string {
beast::tcp_stream stream(ioc);
beast::error_code ec;
auto const results = asio::ip::tcp::resolver(ioc).resolve(host, std::to_string(port));
stream.connect(results, ec);
if (ec) throw std::system_error(ec);
http::request<http::string_body> req{http::verb::get, target, 11};
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
http::write(stream, req, ec);
if (ec) throw std::system_error(ec);
beast::flat_buffer buffer;
http::response<http::dynamic_body> res;
http::read(stream, buffer, res, ec);
if (ec) throw std::system_error(ec);
return beast::buffers_to_string(res.body().data());
});
}
};
int main()
{
asio::io_context ioc;
auto fut = http_get("example.com", "/", 80, ioc)();
ioc.run();
std::cout << fut.get() << std::endl;
}
代码要点:
co_spawn用于在协程里发起异步操作。awaitable封装了asio::yield_context,让我们在协程内部使用co_await等待 IO 完成。promise_type负责把协程结果返回给std::future,让调用者可以像同步代码一样获取结果。
6. 协程与线程池的融合
在高并发场景下,可以把协程与线程池结合使用,利用事件循环调度协程,避免频繁的线程切换。Boost.Asio、cppcoro、cppcoro-future 等库提供了成熟的协程调度器。实现思路:
- 将协程包装成
std::future或cppcoro::task。 - 通过
io_context或自定义调度器,将协程挂起/恢复交由线程池中的工作线程处理。 - 只在 IO 或 CPU 密集型任务时切换协程,保持低开销。
7. 性能注意事项
- 避免不必要的堆分配:协程的 Promise 对象在堆上分配,尽量使用返回值优化(RVO)。
- 精确控制挂起点:每一次
co_await都会创建一个 awaiter,若频繁调用可能导致大量对象生命周期管理。 - 使用
suspend_always/suspend_never:在不需要挂起的场景手动使用suspend_never,减少调度开销。
8. 结语
C++20 的协程为异步编程提供了接近同步的语义,极大降低了编写高并发代码的门槛。掌握其基础概念后,可进一步探索协程的高级用法:协程生成器、协程管道、错误传播、资源管理等。通过结合现代网络库(如 Boost.Asio、cppcoro 等),你可以构建高性能、易维护的异步系统,满足当今复杂业务的需求。祝你在协程的海洋里畅游无阻!