C++20标准引入了协程(coroutines)这一强大的语言特性,提供了一套统一的异步编程模型。与传统的回调或线程池相比,协程让异步代码更接近同步写法,极大提升可读性和维护性。本文以文件读取为例,演示如何使用C++20协程实现异步IO。
1. 关键概念回顾
| 关键术语 | 说明 |
|---|---|
co_await |
等待一个可等待对象(awaitable)完成,并返回其结果。 |
co_yield |
在生成器协程中产生一个值,暂停执行直到下次 co_await。 |
co_return |
结束协程并返回值。 |
std::experimental::generator |
标准库提供的生成器实现(可在 std::experimental 命名空间下使用)。 |
2. 设计异步读取接口
我们先定义一个可等待对象 async_read,它包装了异步文件读取操作:
#include <fstream>
#include <string>
#include <future>
#include <coroutine>
#include <iostream>
struct async_read {
struct promise_type {
std::string result;
std::future<std::string> get_return_object() {
return promise.get_future();
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_value(std::string value) {
result = std::move(value);
}
void unhandled_exception() {
std::terminate();
}
};
std::future<std::string> promise;
};
async_read async_read_file(const std::string& path) {
co_return []() -> std::string {
std::ifstream in(path, std::ios::binary);
if (!in) return "";
return std::string((std::istreambuf_iterator <char>(in)),
std::istreambuf_iterator <char>());
}();
}
promise_type用于存储协程的结果。co_return把读取的内容返回给协程的调用者。promise.get_future()提供一个std::future,调用者可以等待或查询状态。
3. 使用协程进行异步读取
int main() {
auto handle = async_read_file("sample.txt");
std::future<std::string> fut = handle.promise;
// 可以在此处执行其他任务
std::cout << "正在读取文件...\n";
// 等待结果
std::string content = fut.get();
std::cout << "文件内容长度: " << content.size() << " 字节\n";
return 0;
}
此代码在 async_read_file 调用时立即返回协程句柄,主线程可以继续执行,而文件读取在后台进行。通过 future.get() 阻塞等待结果,或者使用 future.wait_for() 进行非阻塞查询。
4. 多文件异步读取
协程天然支持并发。我们可以一次启动多个 async_read_file 并使用 std::vector<std::future<std::string>> 收集结果:
std::vector<std::string> paths = {"a.txt", "b.txt", "c.txt"};
std::vector<std::future<std::string>> futures;
for (const auto& p : paths) {
auto handle = async_read_file(p);
futures.push_back(std::move(handle.promise));
}
for (auto& fut : futures) {
std::string data = fut.get();
std::cout << "文件读取完成, 大小: " << data.size() << '\n';
}
这样每个文件的读取都在各自的协程中异步进行,充分利用多核 CPU 的并行性。
5. 与标准库协程适配
C++20 标准库提供了 std::experimental::generator 与 std::future 的互操作方式。若想将异步IO与生成器结合,例如逐行读取文件:
#include <experimental/generator>
std::experimental::generator<std::string> async_lines(const std::string& path) {
std::ifstream in(path);
std::string line;
while (std::getline(in, line)) {
co_yield line; // 每读取一行返回
}
}
此生成器可以在异步上下文中使用 co_await 读取每一行,而不需要一次性读完全部内容。
6. 小结
- 协程 让异步代码可读性大幅提升。
async_read_file展示了如何包装异步IO为可等待对象。- 多协程并发 通过
std::future或generator轻松实现。 - 与标准库 结合使用
std::experimental组件,代码更简洁、类型安全。
在 C++20 环境下,协程已成为处理高性能异步 IO 的首选方案。通过以上示例,你可以快速上手并将协程融入自己的项目中,进一步提升代码质量与运行效率。