转自:http://hi.baidu.com/winstonzh/blog/item/58ffb78f48e5bfeef01f36b7.html
最近,偶在ACE_Reactor框架和ACE_Proactor框架的基础上写了一个网络应用框架,主要目的是将网络数据收发,网络错误处理,以及网络超时这些socket级的问题与应用逻辑分开,可以保证在应用逻辑的实现不变的情况下,随意改变网络层的实现(也就是用同步IO的ACE_Reactor框架或者用ACE_Proactor框架实现socket上的数据收发,超时及错误处理)。写这个网络应用框架的过程中遇到了很多问题,不得不去看ACE_Reactor框架和ACE_Proactor框架的源代码,所以最后对这两个框架有了更深的认识。下面说说ACE_Reactor框架,都是我个人使用的经验,如有不妥还请指教。
现有的ACE库中提供了三种主要的Reactor实现(更多新的实现到官方网站上应该可以找到),ACE_Select_Reactor, ACE_TP_Reactor和ACE_WFMO_Reactor。三种实现的使用都有不同的注意事项:
(1)ACE_WFMO_Reactor:这个实现是基于Win32的Socket Event-select模型,也就是用一个win32 的手动重置event与一个socket相关联(调用WSAEventSelect函数并指明关心哪些socket事件,如可读,可写等),用WaitForMutiple0bjects来实现多路IO监视,当这个函数返回后调用WSAEnumNetworkEvents检查socket上发生了那种事件(可读或可写)。 这个实现问题最大。 首先,它的致命伤在于每一个ACE_WFMO_Reactor只能同时监视64个socket(具体说是62个,有2个用于notify()和参与event-loop线程的唤醒同步);其次,就是它的可写条件非常诡异,只有当socket内部写缓冲区从满到不满时才算可写,因此,向它register_event 写事件时不出意外的话一定出错(第一次写成功,以后就再写不了了)。因此,这个实现用途很有限。不过,这个实现可以保证多个线程同时对同一个reactor执行event_loop,为了达到这个目的,它在多线程同步上下了很多工夫,读它的源代码会是很好的学习机会(牛人写的就是不一样啊,win32 event的使用简直是登峰造极)。
(2)ACE_Select_Reactor:这个实现中归中举,不要忘记在线程执行event_loop前成为这个reactor的owner。值得学习的是实现中使用的那把锁的实现, 即ACE_Token类,以后自己写程序的时候可以用得上,比mutex强很多。另外就是notify()的实现,也就是那个ACE_Pipe类,说白了就是本地回环socket来实现线程间通信。这个实现和(1)可以在其他线程(也就是没有执行Reactor_Event_Loop的线程)中安全的调用Remove_Handler,这个很方便,一般就可以在handle_close()方法里面做清理handler的工作。但是有一个问题注意,要自己跟踪每个handler注册了那些事件,只有向reactor注销了这些时间后才能delete这个handler。这个问题在C++ NPv2中强调过。这个实现默认是可以同时监视1024个socket,不过可惜的是只能一个线程执行Reactor_Event_Loop。
(3)ACE_TP_Reactor:这个是我最喜欢的了,但是如果不清楚它的实现的话会很容易出错。如果你使用了多个线程run_reactor_event_loop:首先,在默认情况下,你不能象前两个实现那样在不需要某个handler的情况下安全的调用remove_handler();其次,你需要在handle_input(),和handle_timeout()以及handle_output(),和handle_timeout()中考虑同步,注意是handle_**put与handle_timeout()之间才有同步问题,而handle_input()和handle_output()之间不用考虑。解决第一个问题的办法是在ACE_Event_handler中启动reference_count,并且改变删除策略:在handle_close()中不进行任何清除工作,而是将ACE_Event_handler的reference_count减一,而清除工作应该在析构函数中进行。那么,delete是谁调用呢---是reactor框架ACE_Event_handler在reference_count为0时帮你调用。总之,这个实现的使用要比其他的复杂。
总之,Reactor的使用远比那几本书上讲的要复杂得多,如果不彻底了解它是怎么实现的,在使用过程中难免会碰到许多问题。所以,我的建议是,看源代码!这样不仅可以深入理解Reactor框架,少犯错误,还可以学习大牛们怎么写程序的,特别是对于设计模式,还有操作系统API的灵活使用。
自己的看法:
1、ACE_WFMO_Reactor部分,向它register_event 写事件时不出意外的话一定出错,应用中没发现,因为这个实现中,事件在当前状态变化时候触发,而不是基于当前状态触发。所以,注册了写标志后,你就一直发送就行了,直到它告诉你不成了,再等待handle_output()被触发。
2、ACE_Select_Reactor部分,不过可惜的是只能一个线程执行Reactor_Event_Loop,这个不准确。准确的说,是在某一时刻只能被一个线程执行。可以使用多个线程,但是每个线程循环中必须使用owner()方法设置所有者线程。