在 C++20 标准中,协程(coroutine)被正式纳入标准库,提供了比传统回调、线程和状态机更轻量、更易读的异步编程模型。本文将系统介绍协程的核心概念、实现机制,以及如何在实际项目中应用协程实现高性能异步 I/O。
1. 协程的基本概念
协程是可以暂停执行并在之后恢复的函数。与传统的函数不同,协程可以在任意点挂起(co_await、co_yield、co_return),并在需要时重新进入执行。协程的调用者与协程本身是解耦的,协程内部维护自己的状态,能够像普通函数一样使用局部变量。
关键字:
co_await:等待一个可等待对象(awaitable)完成后继续执行。co_yield:产生一个值给调用者,然后暂停执行。co_return:返回一个值并终止协程。
2. Awaitable 与 Awaiter
C++20 对可等待对象做了严格的抽象:
- Awaitable:一种对象,满足
await_ready()、await_suspend()和await_resume()成员函数。 - Awaiter:
await_ready判断是否需要挂起;await_suspend负责挂起协程,并可在挂起前注册回调;await_resume在恢复时返回值。
2.1 标准 awaitable 的例子
#include <chrono>
#include <coroutine>
#include <iostream>
struct SleepAwaitable {
std::chrono::milliseconds duration;
bool await_ready() const noexcept { return duration.count() == 0; }
void await_suspend(std::coroutine_handle<> h) {
std::this_thread::sleep_for(duration);
h.resume(); // 直接恢复
}
void await_resume() const noexcept {}
};
struct Timer {
std::chrono::milliseconds ms;
SleepAwaitable operator co_await() { return {ms}; }
};
3. 协程返回类型:std::future、std::generator
3.1 std::future
`std::future
` 在 C++20 标准库里提供了一个协程返回值包装器,允许协程在完成后返回一个值给调用者。 “`cpp #include std::future async_add(int a, int b) { co_return a + b; // 直接返回 } “` ### 3.2 `std::generator` `std::generator ` 用于产生一系列值,类似于 Python 的 generator。 “`cpp #include std::generator count(int n) { for (int i = 0; i #include #include #include #include using boost::asio::awaitable; using boost::asio::use_awaitable; using boost::asio::ip::tcp; namespace this_coro = boost::asio::this_coro; // 读取文件的协程 awaitable read_file(const std::string& path) { std::ifstream file(path, std::ios::binary); if (!file) co_return “”; // 读取失败 std::string data((std::istreambuf_iterator (file)), std::istreambuf_iterator ()); co_return data; } // 处理客户端连接的协程 awaitable session(tcp::socket socket) { try { // 读取 HTTP 请求(简化) std::string request; while (co_await socket.async_read_some(boost::asio::buffer(request), use_awaitable)) {} // 异步读取文件 std::string body = co_await read_file(“index.html”); // 发送响应 std::string response = “HTTP/1.1 200 OK\r\nContent-Length: ” + std::to_string(body.size()) + “\r\n\r\n” + body; co_await boost::asio::async_write(socket, boost::asio::buffer(response), use_awaitable); } catch (std::exception& e) { std::cerr do_accept; do_accept = [&]() { acceptor.async_accept(use_awaitable).then([&](awaitable a) { auto socket = a.get(); boost::asio::spawn(io_context, std::bind(session, std::move(socket))); do_accept(); // 继续接受 }); }; do_accept(); io_context.run(); } “` 上述示例中: – `read_file` 是一个纯粹的协程,只是同步读取文件后返回,演示如何将同步操作封装成协程。 – `session` 读取客户端请求并异步发送响应,整个处理流程保持同步的阅读体验,却真正以非阻塞方式完成。 ## 5. 性能与注意事项 1. **协程本地状态**:协程的状态被保存在堆上(默认),但可以通过 `co_yield` 的返回值或 `co_return` 进行优化。 2. **异常传播**:协程内部抛出的异常会在 `co_await` 的 awaiter 中捕获并重新抛出。 3. **调试难度**:由于协程在多个挂起点展开,调试时栈帧不连续,建议使用支持协程的 IDE 或工具。 ## 6. 结语 C++20 协程为异步编程提供了更自然、更高效的手段。它既可以在高性能网络服务、游戏引擎中作为核心技术,也可以在嵌入式系统里替代传统的事件循环。掌握协程的基本语法、awaitable 机制以及与标准库(如 `std::future`、`std::generator`)的配合,能够让你在现代 C++ 开发中快速写出既易读又高效的异步代码。