网狐的通过队列实现异步服务的结构理解
核心是使用(IOCP)模型来实现
如上图线程循环中的GetQueuedCompletionStatus,它是 Windows 系统提供的核心 API,用于I/O 完成端口(IOCP)模型中,专门等待异步 I/O 操作(或自定义任务)的完成通知,是高性能网络 / 服务编程的关键接口。
核心功能
阻塞等待完成通知:调用该函数后,线程会阻塞(可设置超时时间),直到以下任一情况发生:
- 有异步 I/O 操作(如套接字读写、文件操作)完成,且结果被投递到关联的完成端口;
- 外部通过
PostQueuedCompletionStatus主动投递自定义 “完成通知”(如代码中投递队列任务); - 超时(若指定了非
INFINITE的超时时间); - 完成端口被关闭 / 出错。
获取完成操作的详细信息:函数通过输出参数返回完成操作的关键信息:
lpNumberOfBytesTransferred:实际传输的字节数(如套接字读取的字节数);lpCompletionKey:关联的 “完成键”(自定义上下文,代码中传递了CQueueService*指针);lpOverlapped:异步操作关联的OVERLAPPED结构体(若为自定义投递则为NULL)。
- 非传统 I/O 场景:代码中并未直接用于网络 / 文件异步 I/O,而是复用 IOCP 模型实现 “任务队列 + 线程池”:
PostQueuedCompletionStatus向完成端口投递 “队列任务通知”(传递CQueueService*作为上下文);GetQueuedCompletionStatus阻塞等待通知,拿到通知后处理队列中的任务(如定时器、数据库、套接字事件);- 利用 IOCP 的 “线程池调度” 特性,实现高效的任务分发(单线程处理队列,也可扩展为多线程)。
关键优势(代码中体现的价值)
- 高效的线程调度:IOCP 会自动优化线程唤醒策略,避免 “线程风暴”,适合高并发场景;
- 统一的任务分发:无论是系统异步 I/O 还是自定义任务(如代码中的队列事件),都能通过同一个接口等待 / 处理;
- 灵活的阻塞 / 超时控制:通过
dwMilliseconds参数(代码中用INFINITE永久阻塞),可灵活控制线程等待逻辑。
总结
GetQueuedCompletionStatus核心是为 IOCP 模型提供 “等待 - 获取” 完成通知的能力,既可以处理系统异步 I/O 操作,也可以像示例代码中一样,被复用为 “自定义任务队列” 的核心调度接口,是 Windows 高性能服务 / 网络编程的基石。
CQueueServiceThread::RepetitionRun:线程的核心循环函数,本质是一个无限循环 + 阻塞等待(不是 “无限循环被调用”,而是函数内部有循环,且循环里被GetQueuedCompletionStatus卡住);CQueueService::AddToQueue:往队列加任务的函数,其内部一定会调用PostQueuedCompletionStatus(IOCP 的投递接口)—— 这才是 “解除阻塞” 的关键。
完整执行流程(一步一步说)
初始状态:线程进入阻塞
RepetitionRun被调用后,函数内部会进入while(true)这类无限循环;- 循环里执行
GetQueuedCompletionStatus,此时没有任何 “完成通知” 投递到完成端口,线程会一直阻塞在这里(卡着不动,不会执行循环的下一轮)。
调用 AddToQueue:解除阻塞的关键
- 当你调用
AddToQueue往队列加任务时,这个函数的核心逻辑是:① 把任务数据写入队列;② 调用PostQueuedCompletionStatus向完成端口主动投递一个 “完成通知”(附带队列上下文、自定义数据等); - 一旦
PostQueuedCompletionStatus调用成功,完成端口就有了 “待处理的通知”,此时: - ✅
GetQueuedCompletionStatus会立即解除阻塞,返回成功; - ✅ 接着执行后续的任务处理逻辑(读取队列数据、调用
OnQueueServiceThread处理任务); - ✅ 任务处理完后,循环会回到
GetQueuedCompletionStatus这一行,再次进入阻塞,等待下一个通知。
- 当你调用
举例说明:
假设模块 A 和模块 B 同时调用AddToQueue,整个流程如下:
补充:关于数据的读写
核心逻辑是通过队列存储:取出队列头部数据
CQueueService::GetData的作用就是从任务队列的队首读取并移除数据:
- 当
AddToQueue把任务(模块 A、模块 B 的)依次加入队列时,任务会按 “先加的在前、后加的在后” 排队; GetQueuedCompletionStatus解除阻塞后,调用GetData会优先取出队列最前面的那个任务(比如先处理模块 A 的,再处理模块 B 的);- 这完全符合 IOCP 模型下 “投递一个通知,处理一个任务” 的逻辑,保证任务按投递顺序处理。
