说IOCP之前,不得不说下基于Overlapped IO模型的另外一种设计思路,上篇说的是基于事件通知的,这篇就说下完成例程,也可以说是回调。基于事件通知,始终有每个线程最多只能监听64个事件的限制,就算经过我上篇写的多线程优化,但线程数始终是有限的,我8G 内存 大概就1500多个线程左右,那么 1500*64=9W6,看上去感觉也足够了,但过多的上下文切换不说,离单机可连接的socket最大值还差很远。一般我做服务器,要求线程数不多余单机线程数的两倍,所以不可能去开这么多线程。接下来就有了另外一种思路,也就是微软以前大力推广的APC回调队列。
先看看百科上的WSARecv,看过上篇文章的应该会发现,WSARecv里面最后一个参数我写的NULL
int WSARecv(
SOCKET s, // 当然是投递这个操作的套接字
,与Recv函数不同
// 这里需要一个由WSABUF结构构成的数组
DWORD dwBufferCount, // 数组中WSABUF结构的数量
LPDWORD lpNumberOfBytesRecvd, // 如果接收操作立即完成,这里会返回函数调用所接收到的字节数
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // 完成例程中将会用到的参数,我们这里设置为 NULL
);
在最后一个参数上绑定完成例程代替事件通知,即与IOEvent可达到一致的效果。维护也更简单,不再需要事件数组,所以client的存储也就不用再与事件保持对应关系,可以生成ID,放在map中,看起来更切合实际。至此,不得不说APC的缺陷:1、若线程被占用,APC队列上的回调将无法得到执行,必须SleepEx,不能阻塞,不能Sleep 2、APC回调执行后还是在执行线程,无法转接到其他线程,所以必须在最开始与accept分离 3、既然SleepEx 是休眠,那么无可避免的是,最开始的时候会有一定延迟,但服务器在热状态的时候几乎不造成影响 4、APC上回调唤醒SleepEx后 函数内部的语句也会执行,所以会有多余的重复检测,我这是(_acceptLock[I].lock();if(_acceptDeque[I].size()==0))5、负载均衡问题成为难题,由于线程里面都是回调,每个回调执行时间是未知的,可能造成一些线程很忙,一些线程比较闲的情况
那么还是来看代码吧:非调试 请关掉输出,不然单个Do 执行速度太慢了,线程内对应的回调都是顺序执行的,具体请看APC队列
server:
// IOApc.cpp: 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
#include
#include
#include
#include
client:
与C++ SOCKET通信模型(一)相同,这里就不再重复粘贴出来