协程(Coroutines)是 C++20 引入的强大语言特性,它们让函数可以在执行过程中挂起并在后续恢复,从而实现异步编程、生成器、事件循环等多种模式。本文将从协程的基本概念入手,逐步演示如何在 C++ 中定义、使用并优化协程。
1. 协程的基本语义
协程本质上是一个可以被多次暂停和恢复的函数。与普通函数不同,协程会在执行到 co_await、co_yield 或 co_return 时挂起,而不是像线程那样切换到另一条执行流。
关键关键词
co_await:挂起当前协程并等待一个 awaitable 对象完成。
co_yield:将一个值返回给调用者,协程挂起,稍后恢复。
co_return:结束协程并返回一个值(如果有返回类型)。
awaitable 对象
一个对象可以被 co_await,只需满足以下两条规则:
operator co_await() 返回一个类型为 Awaiter 的对象。
- Awaiter 必须实现
await_ready(), await_suspend(), await_resume() 三个成员函数。
标准库提供了许多 ready-made awaitable,如 std::suspend_always, std::suspend_never,以及 std::experimental::coroutine_handle.
2. 协程的返回类型
协程函数必须返回 std::future、std::promise、std::generator(实验性)或自定义类型。最常见的是 `std::future
`。如果你想实现一个生成器,C++20 里有 `std::generator`(需要包含 “),但在标准库实现还不完全稳定。
示例:
“`cpp
#include
#include
#include
struct Task {
struct promise_type {
Task get_return_object() { return {}; }
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
};
Task simple_coroutine() {
std::cout
#include
#include
namespace asio = boost::asio; // 或者使用 std::asio
using asio::awaitable;
using asio::co_spawn;
using asio::detached;
using asio::use_awaitable;
awaitable async_read_file(const std::string& path) {
asio::io_context& ctx = co_await asio::this_coro::executor;
asio::posix::stream_descriptor file(ctx);
std::error_code ec;
// 打开文件
file.assign(open(path.c_str(), O_RDONLY), ec);
if (ec) {
throw std::system_error(ec);
}
// 读取文件内容
std::vector
buffer(1024);
std::size_t total = 0;
for (;;) {
std::size_t n = co_await file.async_read_some(asio::buffer(buffer), use_awaitable);
if (n == 0) break; // EOF
total += n;
// 处理 buffer 前 n 个字节
}
file.close();
co_return total;
}
int main() {
asio::io_context ctx;
co_spawn(ctx, async_read_file(“test.txt”), [](std::exception_ptr ep) {
if (ep) std::rethrow_exception(ep);
});
ctx.run();
}
“`
### 说明
– `awaitable
` 是一个封装好的协程返回类型,使用 `asio::use_awaitable` 作为完成句柄。
– `co_spawn` 用来在 `io_context` 上执行协程。
– `co_await` 让协程挂起,等待异步 I/O 完成。
## 4. 性能注意事项
1. **堆栈大小**:协程的挂起点会保存局部状态,通常会在堆上分配栈帧。过多的协程可能导致内存碎片,需要使用 `std::stack` 或 `asio::execution_context` 自定义堆栈管理。
2. **协程对象的拷贝**:默认情况下,协程返回对象是不可移动的,需自定义 `promise_type` 的 `get_return_object` 以返回可移动对象。
3. **异常传播**:在协程中使用 `try/catch` 捕获异常并在 `promise_type` 中通过 `unhandled_exception()` 处理,确保异常能正确传递。
## 5. 小结
– 协程是 C++20 的重要特性,适用于异步编程、生成器和事件驱动模型。
– 必须使用 `co_await`、`co_yield`、`co_return` 关键字,并返回可协程的类型。
– 通过 `asio::awaitable` 可以与 Boost.Asio 或 libuv 等网络库无缝配合,实现高性能的异步 I/O。
– 在使用协程时需关注堆栈管理、对象移动和异常处理,以获得最佳性能和可维护性。
以上内容从基础概念到实际应用,帮助你快速上手 C++20 协程,打造更简洁、高效的异步代码。祝编码愉快!