C++标准库无Proactor实现,真实Proactor需OS支持:Windows用IOCP,Linux需io_uring(5.1+);libuv通过统一完成回调API模拟Proactor语义,而Boost.Asio的async_xxx跨平台行为不一致。

Proactor模式在C++里没有标准库实现
标准C++不提供Proactor抽象,也没有io_context::run_proactor()这类接口。你实际能用的只有std::async(伪异步)、std::thread(线程池模拟)或第三方库封装。真正意义上的Proactor——即操作系统完成I/O后主动回调、用户代码不轮询也不阻塞等待——必须依赖底层支持:Windows上是IOCP,Linux上需io_uring(5.1+)或通过epoll+线程池“模拟”(本质是Reactor变种)。直接写裸Proactor容易误以为自己在用Proactor,其实只是把回调扔给线程池执行。
用libuv写Proactor风格代码最接近真实语义
libuv虽底层在Linux用epoll,但API设计强制统一为完成端口风格:所有uv_*_t句柄操作(如uv_tcp_t)都要求传入uv_read_cb或uv_write_cb,回调触发时数据已就绪或写入完成。这不是语法糖,而是强制你按“操作发起→等完成通知”流程编码:
uv_tcp_t socket;
uv_tcp_init(loop, &socket);
uv_tcp_connect(&req, &socket, (const struct sockaddr*)&addr, on_connect);
<p>// on_connect 回调中才真正拿到连接结果
void on_connect(uv_connect_t<em> req, int status) {
if (status == 0) {
uv_read_start((uv_stream_t</em>)&socket, alloc_buffer, on_read);
}
}</p>关键点:
-
uv_connect立即返回,不阻塞;on_connect才是连接成败的唯一判断点 -
uv_read_start注册后,on_read被调用时buf里已有完整可用数据 - 不要在回调外检查
uv_is_closing()之类状态——Proactor语义下,状态只存在于回调上下文
自己封装Proactor要避开IOCP和io_uring的坑
若坚持手写,Windows下用CreateIoCompletionPort、Linux下用io_uring_queue_init,最容易错的是资源生命周期管理:
立即学习“C++免费学习笔记(深入)”;
- 提交
WSASend或io_uring_prep_send后,对应的WSAOVERLAPPED或io_uring_sqe结构体必须全程有效,直到完成回调返回——不能是栈变量或短生命周期对象 - 完成端口句柄(
HANDLE或int ring_fd)必须在所有I/O提交结束前保持打开,提前CloseHandle或close会导致未完成请求丢失通知 -
io_uring的IORING_SETUP_IOPOLL标志会让内核轮询设备,但仅适用于直连NVMe等低延迟设备,普通TCP socket开启反而降低性能
别把Boost.Asio的async_xxx当成Proactor
boost::asio::ip::tcp::socket::async_read_some看起来像Proactor,但它在Linux默认走epoll(Reactor),Windows才走IOCP。是否真用Proactor取决于编译时定义和运行时平台:
- 没定义
BOOST_ASIO_HAS_IOCP,即使在Windows上也会退化为线程池+select - Linux下即使启用
io_uring后端(需BOOST_ASIO_HAS_IO_URING),也要手动调用use_uring构造io_context,否则仍是epoll - 回调函数签名一致,但背后调度模型不同——这意味着跨平台时,高并发下的延迟分布、CPU占用模式、错误码含义都可能突变
真正难的不是写一个async_accept函数,而是让accept失败时的ec在Windows和Linux上语义对齐,以及确保deadline_timer和网络操作共享同一套完成通知机制。


















