C++ 协程全面解析
通过`await_ready()`/`await_suspend()`/`await_resume()`控制。- 小协程可使用`std::noop_coroutine_promise`- **可挂起(suspend)**和**恢复(resume)**的执行流。1. **`std::generator`** - 标准库提供的生成器。- 调用`get_return_object()`获取返回值。- 遇
# C++ 协程全面解析
协程(Coroutines)是C++20引入的重要特性,它提供了一种全新的控制流机制,能够简化异步编程和复杂控制流的实现。
## 1. 协程基础概念
### 什么是协程?
协程是一种可以暂停执行并在之后恢复的函数,它具有以下特点:
- **可挂起(suspend)**和**恢复(resume)**的执行流
- **保持状态**:恢复时保持挂起前的状态
- **协作式多任务**:由协程主动让出控制权
### 与传统函数对比
| 特性 | 传统函数 | 协程 |
|------|----------|------|
| 调用方式 | 同步调用 | 可异步调用 |
| 执行流 | 一次性执行完 | 可暂停和恢复 |
| 状态保持 | 无 | 有 |
| 栈管理 | 使用调用栈 | 可定制内存管理 |
## 2. C++20协程核心组件
### 协程相关关键字
- `co_await`:暂停协程执行,等待操作完成
- `co_yield`:暂停并返回一个值
- `co_return`:结束协程执行并返回值
### 协程框架类型
协程依赖于几个必须定义的接口类型:
```cpp
struct MyCoroutine {
// 必须定义的接口类型
struct promise_type {
MyCoroutine get_return_object();
std::suspend_always initial_suspend();
std::suspend_always final_suspend() noexcept;
void unhandled_exception();
void return_void(); // 或 return_value
};
// 协程句柄
std::coroutine_handle<promise_type> handle;
};
```
## 3. 协程工作流程
1. **协程创建**
- 分配协程帧(coroutine frame)
- 调用`get_return_object()`获取返回值
- 调用`initial_suspend()`决定是否立即执行
2. **协程执行**
- 遇到`co_await`/`co_yield`时暂停
- 通过`await_ready()`/`await_suspend()`/`await_resume()`控制
3. **协程结束**
- 调用`final_suspend()`决定是否自动销毁
- 销毁协程帧
## 4. 简单协程示例
### 生成器(Generator)实现
```cpp
template<typename T>
struct Generator {
struct promise_type {
T value_;
Generator get_return_object() {
return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
std::suspend_always yield_value(T value) {
value_ = value;
return {};
}
void return_void() {}
};
std::coroutine_handle<promise_type> handle_;
explicit Generator(std::coroutine_handle<promise_type> h) : handle_(h) {}
~Generator() { if (handle_) handle_.destroy(); }
T value() const { return handle_.promise().value_; }
bool next() {
if (!handle_.done()) {
handle_.resume();
}
return !handle_.done();
}
};
Generator<int> range(int start, int end) {
for (int i = start; i < end; ++i) {
co_yield i; // 暂停并返回值
}
}
// 使用
auto gen = range(1, 5);
while (gen.next()) {
std::cout << gen.value() << " "; // 输出: 1 2 3 4
}
```
## 5. 异步任务示例
```cpp
struct AsyncTask {
struct promise_type {
int result_;
AsyncTask get_return_object() {
return AsyncTask{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
void return_value(int value) { result_ = value; }
};
std::coroutine_handle<promise_type> handle_;
explicit AsyncTask(std::coroutine_handle<promise_type> h) : handle_(h) {}
~AsyncTask() { if (handle_) handle_.destroy(); }
int get_result() {
if (!handle_.done()) {
handle_.resume();
}
return handle_.promise().result_;
}
};
AsyncTask compute_async() {
int result = 0;
// 模拟耗时计算
for (int i = 0; i < 1000000; ++i) {
result += i;
}
co_return result; // 返回计算结果
}
// 使用
auto task = compute_async();
std::cout << "Result: " << task.get_result();
```
## 6. 协程与现有库集成
### 与Asio集成(网络编程)
```cpp
#include <asio.hpp>
#include <asio/experimental/awaitable_operators.hpp>
using asio::ip::tcp;
using namespace asio::experimental::awaitable_operators;
asio::awaitable<void> session(tcp::socket socket) {
try {
char data[1024];
for (;;) {
auto n = co_await socket.async_read_some(
asio::buffer(data), asio::use_awaitable);
co_await async_write(socket,
asio::buffer(data, n), asio::use_awaitable);
}
} catch (std::exception& e) {
std::printf("Session exception: %s\n", e.what());
}
}
asio::awaitable<void> listener(asio::io_context& ctx) {
auto executor = co_await asio::this_coro::executor;
tcp::acceptor acceptor(executor, {tcp::v4(), 5555});
for (;;) {
auto socket = co_await acceptor.async_accept(asio::use_awaitable);
asio::co_spawn(ctx, session(std::move(socket)), asio::detached);
}
}
int main() {
asio::io_context ctx;
asio::co_spawn(ctx, listener(ctx)), asio::detached);
ctx.run();
return 0;
}
```
## 7. 协程性能优化
1. **协程帧分配优化**
- 使用`operator new`重载或自定义分配器
- 小协程可使用`std::noop_coroutine_promise`
2. **避免不必要的挂起**
- 检查`await_ready()`提前返回
- 对已知完成的操作直接返回
3. **内存局部性优化**
- 将相关协程放在连续内存中
- 使用协程池管理大量协程
## 8. 协程最佳实践
1. **资源管理**
- 确保协程最终被销毁
- 使用RAII包装协程句柄
2. **错误处理**
- 统一异常处理机制
- 提供取消协程的能力
3. **调试技巧**
- 使用协程调试工具(如Visual Studio协程视图)
- 记录协程生命周期事件
## 9. C++23协程改进
1. **`std::generator`** - 标准库提供的生成器
```cpp
std::generator<int> range(int start, int end) {
for (int i = start; i < end; ++i) {
co_yield i;
}
}
```
2. **协程TR(Technical Report)**
- 更完善的协程工具库
- 标准化的协程模式
3. **协程堆分配优化**
- 更灵活的内存管理策略
- 协程帧复用机制
C++协程为异步编程带来了革命性的改变,虽然学习曲线较陡,但掌握后能极大简化复杂控制流的实现。随着标准库对协程支持的不断完善,它将成为C++高性能并发编程的重要工具。
更多推荐
所有评论(0)