WFMO
WFMO_Reactor是Windows下ACE_Reactor的默认实现(为什么不选择ACE_Select_Reactor作为默认实现,可能是基于效率和功耗的考虑)。WFMO_Reactor下层使用的函数有WaitForMultipleObjects和WSAEventSelect、WSAEnumNetworkEvents。WaitForMultipleObjects函数用于处理线程、互斥、信号灯、事件、定时器和其他事件,而WSAEventSelect用于处理网络IO事件。
由于Windows API和操作系统的不同特点,WFMO_Reactor在很多地方的性能与其他平台不一致。
[注]其实这两个问题在4.4 c++网络编程第二册中的ACE_WFMO_Reactor类——ACE与框架的系统化复用中有解释。希望大家可以帮我编辑一下考试。
1 WFMO_Reactor只能处理62个句柄
由于WaitForMultipleObjects不是一个处理大量事件的函数,它最多处理64个事件句柄,而WFMO_Reactor本身使用2个句柄进行处理,所以一个WFMO_Rector对象只能处理。
如果要做大规模的网络访问,62个事件句柄显然是不够的,尤其是同时处理IO事件的时候。造成这种缺陷的原因应该是WFMO_Reactor的设计者的选择。在赋予WFMO_Reactor强大功能的同时,WFMO_Reactor的设计者只能让网络IO事件的数量受委屈。
2 WRITE_MASK触发机制
WFMO_Reactor选择Windows的WSAEventSelect函数作为网络IO的反应器。但是WSAEventSelect函数的FD_WRITE的事件处理与传统的IO反应器(Select)不同。以下是对MSDN的描述。
FD _ WRITE网络事件的处理略有不同。当套接字第一次用connect/ WSAConnect连接或用accept/WSAAccept接受时,记录FD_WRITE网络事件,然后在WSAEWOULDBLOCK发送失败且缓冲区空间可用后,记录FD _ WRITE网络事件。因此,应用程序可以假设从第一个FD_WRITE网络事件设置开始发送是可能的,并持续到发送返回WSAEWOULDBLO CK。失败后,应用程序会发现,当记录了FD_WRITE网络事件并设置了相关事件对象时,可以再次发送。
简单的翻译就是WSAEventSelect只会在三种情况下发出FD_WRITE通知。第一,套接字使用connect或WSAConnect成功建立连接后;第二种是使用accept或WSAAccept,在套接字被接受之后;第三,如果send、WSASend、sendto或WSASendTo函数未能返回,并且错误是WSAEWOULDBLOCK错误,则缓冲区的空空间再次变得可用。【注意】
【注意】这种触发模式在IO反应器或者IO复用模型中应该叫做边沿触发模式。选择功能似乎不是这样触发的,而是水平触发的。Epoll支持这种方式,但默认情况下还是水平触发。这种方式可能效率更高,但是代码更难写。
可以理解为WSAEventSelect认为套接字基本上是可写的,它认为你应该大胆地发送。只有在WSAEWOULDBLOCK在send中发生故障后,才需要使用WSAEventSelect反应器。
所以对于WFMO_Reactor来说,你不能靠注册(或者唤醒)IO句柄来写,WMFO_Reactor大概也不会回调你的handle_output函数。
[注意]对于网络套接字,只要缓冲区中有空就可以直接发送。除非缓冲区中没有空,否则可能会发生阻塞错误。所以直接发送失败的可能性很小。另外,反复调用注册IO句柄等操作,其实也很费时间。其实,先发送,如果发送失败,那么向反应堆注册IO hsendle应该是更高效的方式,发送高压通信服务器应该选择这种写入方式。
通过这种改造,通信服务器的性能提高了15%左右(CPU利用率降低)。
由于WFMO_Reactor的这些特点,Reactor的可移植性实际上受到了很大的限制。其实我个人感觉如果你对系统特性没有那么多要求的话,在Windows下选择Select_Reactor比WFMO_Reactor是更好的选择。
0条评论