真正的“极速异步文件io”必须对接os原生异步机制(如linux的io_uring或windows的overlapped),而非用co_await包装同步接口;否则只是假异步,无性能提升。

不能直接用 co_await 对 std::ifstream 或裸 fread 调用——它们是同步阻塞接口,协程挂起后线程仍在原地等,根本没提速。 真正的“极速异步文件 IO”必须绕过标准库的阻塞层,对接 OS 原生异步机制(如 Linux 的 io_uring 或 Windows 的 OVERLAPPED),再通过自定义 awaiter 把控制权交还给协程调度器。
为什么 std::fstream + co_await 会假异步
常见误区是这样写:
task<:string> async_read_bad(const std::string& path) {
std::ifstream f(path); // 同步打开
std::string s;
f >> s;
co_return s; // 没有真正异步,只是协程语法糖
}</:string>
问题在于:std::ifstream::operator>> 是完全同步的,整个调用期间线程被独占,协程状态机虽在运行,但底层无并发能力。性能和单线程阻塞读无区别。
- MSVC/Clang/GCC 的 STL 都未对
std::fstream提供co_await支持,operator co_await()不存在 - 即使封装成
std::thread+co_await,也违背协程轻量初衷:每文件启一线程,上下文切换开销反超收益 - 错误日志可能不报错,但
perf top会清晰显示read系统调用长期占据 CPU 样本
必须用 io_uring(Linux)或 I/O Completion Ports(Windows)做底座
极速的关键是零拷贝、批量提交、内核态通知——这些只有 OS 原生异步接口能提供。C++20 协程只负责“怎么写”,不负责“怎么跑”。
- Linux 下优先选
io_uring:支持IORING_OP_READ直接提交读请求,完成时通过io_uring_cqe通知,无需系统调用进出 - Windows 下必须用
CreateFile+FILE_FLAG_OVERLAPPED+ReadFileEx,再包装为可等待对象 - 跨平台方案(如 asio)若未启用
io_uringbackend,默认仍走epoll+read,属于“伪异步”,吞吐上限低 - 编译时需显式链接:
-luring(Linux)、libiocp.lib(Windows),漏掉则io_uring_setup失败返回 -1
手写 awaiter 的三个函数不能乱填
co_await 不是魔法,它严格按协议调用你对象的三个成员函数。填错一个,协程就卡死或崩溃。
-
await_ready():返回true表示立刻完成(比如缓存命中),此时跳过挂起;对磁盘读一律返回false -
await_suspend(std::coroutine_handle h):必须把h保存进io_uring_sqe的user_data字段,或传给ReadFileEx的lpOverlapped;切勿在此函数里h.resume(),否则变成同步 -
await_resume():必须返回与协程声明一致的类型(如ssize_t),且不能抛异常——异常会被静默吞掉;实际错误应通过io_uring_cqe::res或GetLastError()检查
协程调度器必须显式 run,且不能共享 io_context
协程本身不带调度器,co_await 只是让出控制权,谁来恢复它?必须有人监听 io_uring 完成队列或 Windows GetQueuedCompletionStatus。
- 每个线程只能有一个活跃的
io_context实例,多协程共用时需加锁或使用 per-thread context - MSVC 下必须开启
/await和/std:c++20;GCC 需-fcoroutines -D_GLIBCXX_CONCEPTS,否则coroutine_handle未定义 - 调试时注意:GDB 对协程栈帧支持有限,
bt可能只显示__coro_resume,真实挂起点得靠日志打点或coroutine_handle::address()手动查 - 最易忽略的坑:
io_uring提交前未调用io_uring_submit,或 Windows 下ReadFileEx后未调用SleepEx(INFINITE, TRUE)等待,协程永远不恢复
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











