# 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++高性能并发编程的重要工具。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐