Reactor和Proactor是根本不同的IO设计:Reactor依赖epoll_wait通知fd就绪后由用户主动read/write,缓冲区全权自管;Proactor(如io_uring/IOCP)由内核完成数据搬运,回调时数据已就位。

Reactor 和 Proactor 在 C++ 工程中不是两个可互换的“库选项”,而是根本不同的 IO 控制流设计——你选错一个,后续所有线程模型、错误处理、缓冲区管理都要重来。
Reactor 的核心是 epoll_wait + 主动 read/write
它依赖内核多路复用通知“哪个 fd 就绪了”,但不帮你搬数据。你的代码必须在回调里立刻调用 read 或 write,否则下次 epoll_wait 还会返回同一个事件(LT 模式),或者直接丢掉新数据(ET 模式下未读完)。
- 典型场景:高并发短连接服务(如 HTTP API)、Redis/Netty 的底层事件循环
- 常见错误:
EAGAIN/EWOULDBLOCK被忽略,导致逻辑卡死;ET 模式下没循环read直到EAGAIN,漏收包 - 缓冲区由你完全控制:
std::vector<char></char>分配、复用、回收都得自己管 - 调试友好:断点打在
read后就能看到原始字节,gdb 里print buf直接可见
Proactor 在 C++ 中基本等于 io_uring 或 Windows IOCP
它要求内核完成整个 IO 搬运:你提交一个 io_uring_sqe 读请求,内核把数据填进你给的 buffer,再发完成事件。你收到的回调里,数据已经就位,read 调用根本不会出现。
- 典型场景:Linux 上大块文件读写(如数据库 WAL)、Windows 服务端(IIS 底层)
- 常见错误:buffer 生命周期早于
io_uring_cqe返回,触发 use-after-free;liburing 版本低于 2.2 时IORING_OP_READV不支持 socket - 跨平台陷阱:POSIX AIO(
aio_read)在 Linux 上其实是线程池模拟,不是真 Proactor,性能和语义都不等价 - 调试困难:数据何时进 buffer、是否被内核覆盖、completion event 是否丢失,全在 kernel ring 里,gdb 看不到中间态
C++ 生态里真正能用的 Proactor 实现极少
Boost.Asio 声称支持 Proactor,但它在 Linux 默认走 epoll + 线程池模拟,在 Windows 才用 IOCP。你写 async_read,实际执行路径取决于编译目标和运行时检测,不是声明即生效。
立即学习“C++免费学习笔记(深入)”;
-
boost::asio::use_awaitable在 Linux 仍可能 fallback 到 Reactor 行为 - liburing 是目前最接近原生 Proactor 的选择,但需手动管理 SQ/CQ ring、内存注册(
io_uring_register_buffers)、避免IORING_SETUP_IOPOLL误用(仅适用于 NVMe) - 如果你用的是
std::async或std::jthread包一层read,那只是线程池 + 阻塞 IO,既不是 Reactor 也不是 Proactor
真正决定模式的不是你用了什么类名,而是 read 这个系统调用出现在哪条执行路径上:在事件回调里?那是 Reactor。压根没出现?那才可能是 Proactor。别被封装层的名字骗了。


















