协程(Coroutines)是 C++20 新增的重要特性之一,它为实现轻量级异步编程提供了语言层面的支持。与传统的基于回调或线程的异步模式相比,协程在语义上更接近同步代码,极大地简化了异步逻辑的书写与维护。下面从概念、实现细节、典型使用场景以及示例代码四个方面,帮助你快速上手协程。
1. 协程基本概念
协程是可以在函数内部挂起(co_yield、co_await、co_return)并在后续恢复的函数。与线程不同,协程的挂起与恢复完全由编译器和标准库协同完成,避免了线程上下文切换的高昂成本。协程内部维护的状态由 promise 对象管理,它负责在挂起点保存局部变量、生成返回值、处理异常等。
2. 关键语言特性
| 关键字 | 作用 | 示例 |
|---|---|---|
co_await |
等待一个可等待对象完成(如 std::future、std::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++ 在异步编程领域的竞争力更上一层楼。