在 C++20 中,协程(coroutine)被正式引入标准库,成为解决异步编程、生成器和协作式多任务等问题的强大工具。本文将从协程的基本概念、实现原理、关键标准库组件以及一个完整的实战案例入手,帮助你快速掌握并使用 C++20 协程。
1. 协程的基本概念
协程是一种轻量级的程序单元,可以在执行过程中暂停(co_await、co_yield、co_return)并在未来某个点恢复。与线程不同,协程不需要独立的栈,而是共享调用栈,因而上下文切换开销极低。协程在 IO、网络、游戏循环等需要大量等待的场景中表现尤为突出。
2. C++20 协程的核心语法
co_await:挂起协程,等待一个 awaitable 对象完成后恢复。co_yield:生成一个值,并挂起协程,等到下一个co_await或resume再恢复。co_return:返回协程最终结果并结束协程。
这些关键字与 std::experimental::coroutine 或者更现代的 std::coroutine 标准库组件配合使用,构成完整的协程体系。
3. 关键标准库组件
| 组件 | 作用 |
|---|---|
std::coroutine_handle |
协程句柄,管理协程生命周期、恢复和销毁 |
std::suspend_always / std::suspend_never |
控制协程挂起行为 |
std::promise / std::future |
与协程配合实现异步结果返回 |
std::generator(C++23) |
生成器类型,简化 co_yield 用法 |
在 C++20,最常见的做法是自己实现一个 awaitable 类型,然后在 operator co_await 中返回适配器,或者使用第三方库如 cppcoro、Boost.Coroutine。
4. 实战案例:异步文件读取
下面给出一个完整示例:使用协程实现异步读取文本文件,每行返回给调用者。示例中使用 asio 进行异步 I/O(或可自行替换为 boost::asio)。
#include <iostream>
#include <string>
#include <vector>
#include <filesystem>
#include <asio.hpp>
#include <coroutine>
using asio::awaitable;
using asio::io_context;
using asio::buffer;
using asio::use_awaitable;
// 简易异步读取一行
awaitable<std::string> async_read_line(asio::streambuf& sbuf, std::istream& in) {
std::size_t n = co_await asio::async_read_until(in, sbuf, '\n', use_awaitable);
std::istream is(&sbuf);
std::string line;
std::getline(is, line);
co_return line;
}
// 主协程:读取文件所有行
awaitable<std::vector<std::string>> read_file_lines(const std::string& path) {
asio::io_context& ctx = co_await asio::this_coro::executor;
asio::posix::stream_descriptor fd(ctx, ::open(path.c_str(), O_RDONLY));
std::istream in(&fd);
asio::streambuf sbuf;
std::vector<std::string> lines;
while (true) {
std::string line = co_await async_read_line(sbuf, in);
if (line.empty() && in.eof()) break; // EOF
lines.push_back(line);
}
co_return lines;
}
int main() {
io_context ctx;
std::string filename = "sample.txt";
// 调用协程并等待结果
auto future = std::async(std::launch::async, [&](){
return co_spawn(ctx, read_file_lines(filename), asio::use_future).get();
});
// 继续做其他工作,模拟并发
std::cout << "主线程继续执行...\n";
// 等待文件读取完成
std::vector<std::string> lines = future.get();
std::cout << "文件读取完成,行数:" << lines.size() << "\n";
for (const auto& l : lines) {
std::cout << l << "\n";
}
return 0;
}
关键点解析
awaitable:标记函数为协程返回类型,自动与asio的异步 API 集成。co_await:挂起当前协程,等待async_read_until完成。co_return:将结果返回给调用者。co_spawn:启动协程,并返回std::future,与asio的事件循环配合使用。
5. 小结
- 协程是 C++20 标准库中最具前瞻性的特性之一,适用于需要大量挂起与恢复的异步场景。
- 通过
std::coroutine_handle、std::suspend_always等基础组件,你可以自行实现 awaitable 类型,实现自定义的异步行为。 - 与
asio等网络/IO 库配合,可以轻松完成高性能异步 I/O、协程化网络服务等。 - 进一步学习可关注 C++23 的
std::generator、std::async的协程改造等新功能。
协程正在改变 C++ 的异步编程范式,掌握它将让你在未来的项目中拥有更高效、更可读的代码。祝你编码愉快!