peakzhang 发表于 2008-5-21 23:16:05

各种Reactor实现的总结

大家好,第一次来这个属于ACEer的地方真的很高兴啊!下面就ACE中现有的三种Reactor做一些总结,都是我个人使用的经验,如有不妥还请指教。
最近,偶在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的灵活使用。关于ACE_Proactor的下次再聊。

peakzhang 发表于 2008-5-21 23:16:14

提一些自己的看法,大家讨论。
1、ACE_WFMO_Reactor部分,向它register_event 写事件时不出意外的话一定出错,应用中没发现,因为这个实现中,事件在当前状态变化时候触发,而不是基于当前状态触发。所以,注册了写标志后,你就一直发送就行了,直到它告诉你不成了,再等待handle_output()被触发。
2、ACE_Select_Reactor部分,不过可惜的是只能一个线程执行Reactor_Event_Loop,这个不准确。准确的说,是在某一时刻只能被一个线程执行。可以使用多个线程,但是每个线程循环中必须使用owner()方法设置所有者线程。

peakzhang 发表于 2008-5-21 23:16:29

我在使用ACE_WFMO_Reactor时,也发现楼主所说的情况,即“就是它的可写条件非常诡异,只有当socket内部写缓冲区从满到不满时才算可写,因此,向它register_event 写事件时不出意外的话一定出错(第一次写成功,以后就再写不了了)”,也就是第一次handle_output()触发后,以后再也没有被触发!

不知道什么原因?

peakzhang 发表于 2008-5-21 23:16:35

不要指望handle_output一直被触发,应该倒过来,你一直写,直到失败了,再等待handle_output被触发。

peakzhang 发表于 2008-5-21 23:16:45

怎样用ACE_Select_Reactor替换ACE_WFMO_Reactor?

我这样用,结果

    ACE_Select_Reactor* sr = new ACE_Select_Reactor(); // 异常!dynamic_cast转型异常
    ACE_Reactor* r = new ACE_Reactor(sr, 1);
    ACE_Reactor::instance(r, 1);

peakzhang 发表于 2008-5-21 23:16:54

Linux程序员可以关注一下ACE_Dev_Poll_Reactor

peakzhang 发表于 2008-5-21 23:17:03

具体如何使用ACE_Dev_Poll_Reactor,本站有文章。主要是编译ACE的时候要注意。

jonathanliu2004 发表于 2008-6-16 21:09:37

可以采用notify方式出发hand_output.

modern 发表于 2009-6-26 17:26:33

提几个问题:
1.看书上与很多资料上都提到ACE_WFMO_Reactor,对写的处理严重依赖于windows平台对于写事件的定义。之前我都是一直调用send放心的发的,汗~~。那么我想知道,如果直接send失败了handle_output会被触发,然后怎么做呢?继续发?如果缓冲区还是不够大,仍然发送失败呢,那么就一直的重复发下去,直到成功么?尽管这种情况现实中,未必会遇到。
2.ACE_TP_Reactor的并发同步问题,貌似楼主提到的和书上说的有差异,书上的意思是说可以并发的排放i/o回调与定时器事件,只是对于相同句柄的I/O在多线程时进行序列化。我最近再读ACE_TP_Reactor 的源代码,对一些细节问题还是有不少疑惑的。
Concurrency considerations. Multiple threads running an ACE_TP_Reactor event loop can process events concurrently on different handles. They can also dispatch timeout and I/O callback methods concurrently on the same event handler. The only serialization in the ACE_TP_Reactor occurs when I/O events occur concurrently on the same handle.
3.如楼主所说ACE_WFMO_Reactor在多线程同步上下了确实很多工夫,读这段代码还是收益匪浅的。

[ 本帖最后由 modern 于 2009-6-28 23:22 编辑 ]

wishel 发表于 2009-6-28 14:24:21

1,“果直接send失败了handle_time_out会被触发”,是说handle_out吧?
我是这样理解的: 可以写的时候一直写,等到出错返回EWOULDBLOCK,就return。等下次可以写的时候,reactor又会dispatch这个handler,接着写。
2,examples\Reactor\TP_Reactor中的readme:
This program demonstrates what we think is a bug in the ACE library. The
affected component is the ACE_TP_Reactor. According to the documentation, the
reactor ensures that only one of the handle_*() methods of an event handler is
called at a time. Tino found this to be not true and I wrote this example
program showing the behavior. I do not exclude the possibility that we are
using the ACE library in an unintended/wrong way. So comments on the code as
well as any other remarks are welcome.

比较汗的是,最新的5.7还是带了这个例子,不知道如果有人认为这是个bug的话为什么不修复?
页: [1] 2
查看完整版本: 各种Reactor实现的总结