C++20协程:让异步编程如呼吸般简单

在C++20中,协程(Coroutines)被正式加入语言核心,为异步编程带来了革命性的改进。相比传统的回调链、事件循环或基于线程的并发模型,协程以轻量、易读的方式隐藏了复杂的状态机,让开发者可以像写顺序代码那样书写异步逻辑。本文将从协程的基本概念、关键语法、实现原理以及常见应用场景入手,帮助你快速掌握C++20协程。

1. 协程到底是什么?

协程是可以在执行过程中挂起并在未来某个时间点恢复的函数。挂起点通过co_awaitco_yieldco_return语句实现。协程内部维护的状态机由编译器自动生成,外部开发者只需要关注业务逻辑。

关键点

  • 挂起(Suspend)co_awaitco_yield
  • 恢复(Resume):协程对象被再次调用或异步事件触发
  • 完成(Completion)co_return或异常抛出

2. 语法要点

// 协程返回类型必须是 std::future <T>、std::generator<T> 或自定义 Awaitable
std::future <int> asyncAdd(int a, int b) {
    // 这里可以先做同步工作
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    co_return a + b; // 结束协程,返回结果
}

std::generator <int> range(int n) {
    for (int i = 0; i < n; ++i) {
        co_yield i; // 挂起,返回一个值
    }
}

2.1 Awaitable对象

协程只能挂起与“可等待”对象。Awaitable必须实现:

bool await_ready() const noexcept;          // 是否立即完成
void await_suspend(std::coroutine_handle<> h); // 挂起时执行
await_resume() const noexcept;               // 恢复时返回值

标准库提供的std::futurestd::promisestd::generator已实现这些接口。

2.2 协程句柄

std::coroutine_handle<>是协程的入口点。通过句柄可以:

  • 调用resume()恢复协程
  • 检查done()是否结束
  • 调用destroy()销毁协程

3. 编译器生成的状态机

协程本质上是一个隐式生成的状态机。编译器会:

  1. 为每个挂起点生成一个状态编号。
  2. 把局部变量放入生成器的“悬挂”结构(heap allocation)。
  3. co_awaitco_yield转换为调用await_suspend()await_resume()
  4. 生成resume()函数,按状态执行对应代码块。

这意味着协程比普通函数更消耗内存(需要堆分配),但挂起与恢复的成本远低于线程切换。

4. 常见应用

4.1 简化网络 I/O

std::future<std::string> fetch(const std::string& url) {
    // 假设socket是 Awaitable
    auto socket = std::make_shared <AsyncSocket>(url);
    co_await socket->connect();
    std::string data;
    while (true) {
        std::string chunk = co_await socket->read(); // 挂起等待数据
        if (chunk.empty()) break; // EOF
        data += chunk;
    }
    co_return data;
}

无需回调链,错误处理也可用try/catch。

4.2 生成异步迭代器

利用std::generator可以像同步迭代器一样遍历异步结果。

for (int x : asyncRange(10)) { // asyncRange 返回 std::generator <int>
    std::cout << x << '\n';
}

4.3 任务调度器

std::future与协程结合,可实现轻量级的协程调度器。任务以co_await方式挂起,调度器在事件到来时恢复。

5. 性能与注意事项

  • 内存占用:协程会在堆上分配悬挂结构,若协程频繁创建需注意泄漏与内存碎片。
  • 异常安全:异常在协程内部会自动传播到co_return点,调用者可捕获。
  • 调试难度:协程代码的控制流被拆分,调试时需要使用IDE的协程支持(如Visual Studio、CLion)。

6. 小结

C++20协程提供了语法糖编译器自动生成的状态机,让异步编程变得既直观又高效。通过std::futurestd::generator以及自定义Awaitable,开发者可以轻松实现非阻塞I/O、流式数据处理、协作式多任务等功能。掌握协程的核心概念和语法后,你将能够将传统繁琐的异步代码转化为简洁、可维护的顺序式代码,极大提升开发效率与代码质量。

发表评论