协程是C++20引入的重要特性,它允许程序在执行过程中暂停并在之后恢复,极大地方便了异步编程、生成器以及状态机的实现。C++20提供了三种主要的协程实现方式:标准协程(co_await/co_yield/co_return)、基于Boost.Coroutine的协程以及使用第三方库(如cppcoro、asio)的协程。下面分别介绍这三种方式的特点、使用场景以及优缺点。
1. 标准协程(co_await/co_yield/co_return)
1.1 基本语法
#include <coroutine>
#include <iostream>
struct Generator {
struct promise_type {
int current_value;
std::suspend_always yield_value(int v) {
current_value = v;
return {};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
Generator get_return_object() { return {}; }
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
std::coroutine_handle <promise_type> handle;
};
Generator range(int start, int end) {
for (int i = start; i <= end; ++i)
co_yield i;
}
1.2 典型用途
- 异步 I/O:配合
std::future或自定义awaiter实现异步网络请求、文件读取。 - 生成器:如上面例子所示,用
co_yield生成序列。 - 状态机:通过
co_await暂停状态,并在外部触发恢复。
1.3 优点与缺点
| 优点 | 缺点 |
|---|---|
| 与标准库完全集成,语法简洁 | 需要C++20编译器支持 |
| 内存开销可控,堆栈大小可配置 | 对初学者学习曲线较陡峭 |
| 可以与其他异步框架无缝对接 | 调试时堆栈信息不直观 |
2. Boost.Coroutine(协程库)
2.1 基本使用
#include <boost/coroutine2/all.hpp>
#include <iostream>
void foo(boost::coroutines2::coroutine <int>::push_type& yield) {
for (int i = 0; i < 5; ++i)
yield(i);
}
int main() {
boost::coroutines2::coroutine <int>::pull_type source(foo);
while (source)
std::cout << source.get() << " ";
return 0;
}
2.2 适用场景
- 兼容性需求:在不支持C++20的项目中使用协程。
- 可定制堆栈:可以自定义堆栈大小、堆栈分配器。
- 与Boost库生态:与
boost::asio、boost::beast等无缝集成。
2.3 优点与缺点
| 优点 | 缺点 |
|---|---|
| 兼容旧编译器(C++11/14/17) | API 较为低级,使用不够直观 |
| 细粒度控制协程行为 | 需要手动管理协程生命周期 |
| 与Boost其他组件深度集成 | 与标准协程不完全兼容,代码迁移成本高 |
3. 第三方协程库(cppcoro、asio、gsl)
3.1 cppcoro
cppcoro是一个轻量级的协程工具库,提供了诸如generator, task, pipeline等抽象。
#include <cppcoro/generator.hpp>
#include <iostream>
cppcoro::generator <int> range(int start, int end) {
for (int i = start; i <= end; ++i)
co_yield i;
}
int main() {
for (auto v : range(1, 5))
std::cout << v << " ";
}
3.2 asio(Boost.Asio的协程支持)
Boost.Asio自C++20起支持co_await,也可以使用asio::awaitable。
#include <boost/asio.hpp>
#include <iostream>
boost::asio::awaitable <void> async_print() {
std::cout << "Start\n";
co_await boost::asio::this_coro::executor;
std::cout << "End\n";
}
3.3 GSL(Guidelines Support Library)协程实验
Microsoft Research的GSL项目提供了一套协程实验性接口,尚处于实验阶段。
3.4 适用场景与优点
- 异步网络编程:asio + coroutine能实现高性能I/O。
- 简化生成器:cppcoro的
generator使用方式与标准协程相似。 - 实验性特性:GSL为实验性协程提供了可测试的实现。
3.5 缺点
- 第三方依赖会增加构建复杂度。
- 与标准协程的兼容性不一,迁移时需注意。
- 一些库仍在维护阶段,更新不及时。
4. 如何选择合适的协程实现?
| 需求 | 推荐实现 |
|---|---|
| 纯粹使用标准C++20功能 | 标准协程 |
| 需要在旧编译器或C++17项目中使用 | Boost.Coroutine |
| 需要高级异步网络或文件I/O | asio + 协程 |
| 想快速原型化生成器、状态机 | cppcoro |
5. 小结
C++20的协程特性为现代C++程序员提供了强大且灵活的异步编程模型。标准协程以其简洁的语法和与标准库的无缝对接成为首选;Boost.Coroutine为需要向后兼容的项目提供了解决方案;而第三方库如cppcoro和asio则在特定领域提供了成熟的实现。根据项目需求、编译器版本以及依赖管理,选择最合适的协程实现将帮助你更高效地构建可维护且性能优秀的 C++ 软件。