C++20 引入了协程(Coroutine)概念,极大地方便了异步编程。相比传统的回调、Future/Promise 或基于事件循环的模型,协程以同步的语法实现异步逻辑,代码更易读、维护成本更低。本文将通过一个简易的框架示例,演示如何使用 C++20 协程来实现异步网络请求,并在此基础上扩展到多路复用、错误处理和超时控制。
1. 设计目标
- 简洁:仅依赖标准库(`
`, “, “)和一个轻量级网络库(本例使用 `asio` 的纯头文件版本)。
- 可组合:协程函数返回
std::future 或自定义 Task 对象,便于链式调用。
- 错误传播:异常在协程链中传播,最终由
std::future 的 get() 捕获。
- 超时:支持
asio::steady_timer 进行超时处理,超时时抛出 std::runtime_error。
2. 依赖与环境
- 编译器:支持 C++20 并开启协程实验特性,例如
-std=c++20 -fcoroutines(GCC/Clang)或 /std:c++20(MSVC)。
- Boost.ASIO:使用
asio.hpp(不必链接 Boost)来完成异步 I/O。
- CMake:示例项目使用 CMake 3.20+。
cmake_minimum_required(VERSION 3.20)
project(async_co_cpp20)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(async_co main.cpp)
find_package(Boost 1.70 REQUIRED COMPONENTS system)
target_link_libraries(async_co PRIVATE Boost::system)
3. 关键实现细节
3.1 `Task
`:协程返回类型
“`cpp
template
struct Task {
struct promise_type {
std::coroutine_handle continuation;
std::exception_ptr exception;
T value;
Task get_return_object() {
return Task{std::coroutine_handle
::from_promise(*this)};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept {
if (continuation) continuation.resume();
return {};
}
void return_value(T v) { value = std::move(v); }
void unhandled_exception() { exception = std::current_exception(); }
void set_continuation(std::coroutine_handle h) { continuation = h; }
};
using handle_type = std::coroutine_handle
;
handle_type coro;
Task(handle_type h) : coro(h) {}
~Task() { if (coro) coro.destroy(); }
T get() {
if (coro.promise().exception)
std::rethrow_exception(coro.promise().exception);
return std::move(coro.promise().value);
}
};
“`
#### 3.2 异步读取与协程协作
“`cpp
Task async_read(Asio::ip::tcp::socket& sock, std::size_t size) {
auto buffer = std::make_shared>(size);
std::promise prom;
auto handler = [prom = std::move(prom), buffer](auto&& self, const boost::system::error_code& ec, std::size_t n) mutable {
if (ec) {
prom.set_exception(std::make_exception_ptr(boost::system::system_error(ec)));
} else {
prom.set_value(std::string(buffer->data(), n));
}
};
sock.async_read_some(boost::asio::buffer(*buffer), handler);
co_return co_await prom.get_future();
}
“`
#### 3.3 超时包装
“`cpp
Task async_read_with_timeout(Asio::ip::tcp::socket& sock, std::size_t size,
std::chrono::steady_clock::duration timeout) {
Asio::steady_timer timer(sock.get_executor());
timer.expires_after(timeout);
std::promise prom;
auto read_handler = [prom = std::move(prom)](auto&& self, const boost::system::error_code& ec, std::size_t n) mutable {
prom.set_value(std::string(self->data(), n));
};
sock.async_read_some(boost::asio::buffer(buffer), read_handler);
auto timer_handler = [prom = std::move(prom)](auto&& self, const boost::system::error_code& ec) mutable {
if (!ec) prom.set_exception(std::make_exception_ptr(std::runtime_error(“timeout”)));
};
timer.async_wait(timer_handler);
co_return co_await prom.get_future();
}
“`
#### 3.4 主协程示例
“`cpp
Task
main_coroutine() {
try {
Asio::io_context io;
Asio::ip::tcp::resolver resolver(io);
auto endpoints = co_await resolver.async_resolve(“example.com”, “80”,
asio::use_awaitable);
Asio::ip::tcp::socket sock(io);
co_await Asio::async_connect(sock, endpoints, asio::use_awaitable);
std::string request = “GET / HTTP/1.1\r\nHost: example.com\r\n\r\n”;
co_await sock.async_write_some(asio::buffer(request), asio::use_awaitable);
std::string response = co_await async_read_with_timeout(sock, 4096,
std::chrono::seconds(5));
std::cout ` 作为返回类型,以获得更完整的 I/O 适配。
后续可以继续实现:
– **连接池**:使用协程实现多路复用连接,减少重连成本。
– **重试策略**:在协程链中实现指数退避重试。
– **多协议支持**:在同一框架中添加 HTTP/2、WebSocket 等。
通过上述示例,读者可以快速入门 C++20 协程的异步网络编程,进而在实际项目中灵活应用。