在C++20中,标准库加入了协程支持,提供了std::future、std::promise等工具,甚至还有更轻量级的std::experimental::generator。下面给出一个完整的异步IO示例:我们使用协程从磁盘读取文件内容,并在读取完成后返回结果,而主线程可以在此期间做其他工作。示例演示了协程的基本语法、co_await、co_return以及自定义awaitable类型的实现。代码分为三部分:文件读取的异步任务、简单的awaitable包装以及主程序。
#include <iostream>
#include <fstream>
#include <string>
#include <future>
#include <thread>
#include <chrono>
#include <coroutine>
// 简单的异步文件读取包装
struct FileReadAwaiter {
std::string path;
std::string result;
bool done = false;
// await_ready:如果立即完成返回true
bool await_ready() const noexcept { return false; }
// await_suspend:协程挂起,并在后台线程读取文件
void await_suspend(std::coroutine_handle<> h) {
std::thread([this, h]() {
std::ifstream fin(path, std::ios::binary);
if (!fin) { result = "文件打开失败"; }
else {
fin.seekg(0, std::ios::end);
std::size_t size = fin.tellg();
fin.seekg(0);
result.resize(size);
fin.read(&result[0], size);
}
done = true;
h.resume(); // 读取完毕后恢复协程
}).detach();
}
// await_resume:返回读取结果
std::string await_resume() { return std::move(result); }
};
// 通过协程实现异步读取文件
std::future<std::string> asyncReadFile(const std::string& path) {
struct Task {
struct promise_type {
std::promise<std::string> prom;
Task get_return_object() {
return Task{ prom.get_future() };
}
std::suspend_never initial_suspend() noexcept { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_value(std::string value) { prom.set_value(std::move(value)); }
void unhandled_exception() { prom.set_exception(std::current_exception()); }
};
std::future<std::string> fut;
Task(std::future<std::string> f) : fut(std::move(f)) {}
std::future<std::string> get_future() { return std::move(fut); }
};
co_return co_await FileReadAwaiter{path};
}
// 主程序
int main() {
std::cout << "开始异步读取文件...\n";
auto fut = asyncReadFile("sample.txt");
// 在这里可以做其他工作
for (int i = 0; i < 5; ++i) {
std::cout << "主线程工作中,循环 " << i+1 << "\n";
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
// 等待异步结果
std::string content = fut.get();
std::cout << "文件内容读取完成,长度: " << content.size() << " 字节\n";
return 0;
}
关键点说明
-
await_ready
该函数判断协程是否可以立即完成。如果返回true,协程会直接执行await_resume而不挂起。这里返回false,始终挂起。 -
await_suspend
该函数接收当前协程句柄h,在这里我们创建一个后台线程完成文件读取。读取完成后通过h.resume()恢复协程。 -
await_resume
协程恢复后会调用此函数,返回最终结果。 -
协程返回
asyncReadFile返回一个std::future<std::string>,在协程内部通过co_await得到读取结果,然后用co_return返回。 -
线程安全
为了简单演示,后台线程直接detach。在生产代码中建议使用线程池或更安全的同步机制。
该示例演示了C++20协程与异步IO的结合,展示了如何在不阻塞主线程的情况下完成文件读取任务。通过适当封装,你可以将此模式扩展到网络请求、数据库查询等场景,从而实现高并发、低延迟的异步程序。