从 C++ 17 到 C++ 20:协程(Coroutines)入门

协程(Coroutines)是 C++20 新增的重要特性之一,它为实现轻量级异步编程提供了语言层面的支持。与传统的基于回调或线程的异步模式相比,协程在语义上更接近同步代码,极大地简化了异步逻辑的书写与维护。下面从概念、实现细节、典型使用场景以及示例代码四个方面,帮助你快速上手协程。

1. 协程基本概念

协程是可以在函数内部挂起(co_yieldco_awaitco_return)并在后续恢复的函数。与线程不同,协程的挂起与恢复完全由编译器和标准库协同完成,避免了线程上下文切换的高昂成本。协程内部维护的状态由 promise 对象管理,它负责在挂起点保存局部变量、生成返回值、处理异常等。

2. 关键语言特性

关键字 作用 示例
co_await 等待一个可等待对象完成(如 std::futurestd::task auto result = co_await asyncFunc();
co_yield 生成一个值给调用者,类似生成器 co_yield i;
co_return 结束协程,返回值 co_return sum;
co_resume 由外部调用恢复协程(在 C++20 中由标准库包装,实际使用时一般不直接调用) handle.resume();

3. 常见协程实现

  • 生成器(Generator):使用 co_yield 产生一系列值,典型应用为逐步产生序列、遍历大集合等。
  • 异步任务(Task):结合 co_await 对异步 I/O、网络请求等进行等待,代码几乎无异步侵蚀。
  • 协程池(Coroutine Pool):通过 std::experimental::coroutine_handle 管理协程的生命周期,适合高并发的轻量级任务。

4. 示例:异步网络请求

下面的代码演示了一个简单的异步 HTTP GET 请求示例,使用 cppcoro(第三方协程库)来模拟 co_await 的网络 I/O。

#include <iostream>
#include <string>
#include <cppcoro/single_thread_context.hpp>
#include <cppcoro/single_waiter.hpp>
#include <cppcoro/awaitable.hpp>
#include <cppcoro/http_client.hpp>

cppcoro::awaitable<std::string> fetch(const std::string& url)
{
    cppcoro::http::Client client;
    cppcoro::http::Response response = co_await client.get(url);
    std::string body = co_await response.readAll();
    co_return body;
}

int main()
{
    cppcoro::single_thread_context context;
    context.post([&]{
        cppcoro::sync_wait(fetch("http://example.com"))
            .then([](const std::string& data){
                std::cout << "Fetched data length: " << data.size() << '\n';
            });
    });
    context.run();
    return 0;
}

说明

  • cppcoro::http::Client 是一个假设的 HTTP 客户端,内部实现利用协程完成非阻塞 I/O。
  • sync_wait 用于在单线程事件循环中等待协程完成并获取结果。

5. 性能与注意事项

  • 协程切换成本低:挂起点仅保存当前上下文(栈帧、寄存器),恢复时快速恢复。
  • 与线程共享资源:协程内部仍然使用共享内存,如果并发访问需加锁或使用原子操作。
  • 异常传播:协程中的异常会被 promise 捕获,并在 co_await 点抛出。需要在调用链上正确捕获。

6. 进一步学习资源

  • 《C++20 新特性》: 详细介绍协程、模块、范围等。
  • cppcoro 官方文档: https://github.com/knight89/cppcoro
  • 《Effective Modern C++》: 对 C++17/20 语言特性进行深入剖析。

通过掌握协程的基本语法与设计理念,你可以在不牺牲可读性的前提下,编写高效、可维护的异步程序。协程的普及将使得 C++ 在异步编程领域的竞争力更上一层楼。

发表评论