找回密码
 用户注册

QQ登录

只需一步,快速开始

楼主: peakzhang

各种Reactor实现的总结

[复制链接]
发表于 2009-6-28 14:30:54 | 显示全部楼层
ACE_WFMO_Reactor 多线程跑的时候需要注意自己处理同步:
Unlike those using select()-based reactors, multithreaded applications can demultiplex and dispatch events concurrently using the ACE_WFMO_Reactor::handle_events() method. [2] In the multithreaded case it's therefore possible that different threads will dispatch events simultaneously to the same event handler.
Event handlers must therefore explicitly protect against race conditions when the handle_events() event loop is executed by multiple threads on the same ACE_WFMO_Reactor object. The ACE_TP_Reactor avoids these race conditions by implementing an internal protocol that automatically suspends a handle before dispatching its event handler. Any follower thread that subsequently becomes the leader doesn't dispatch events on the affected handle until the callback is complete and the handle is resumed.

sidebar26又说由于ACE_WFMO_Reactor 的deferred registration change机制,The ACE_TP_Reactor的同步协议不适用于它。

个人看法:
如果不需要同步(如UDP),ACE_WFMO_Reactor可以选择,因为它是真正的多线程跑event loop。而The ACE_TP_Reactor在同一时刻只能是一个leader在跑event loop,众多worker thread并发的跑event handler。
如果需要同步,The ACE_TP_Reactor是更好的选择,因为不需要用户再去实现同步机制。而如果用ACE_WFMO_Reactor,用户自己实现的同步机制会比较复杂,其性能也可能会有问题。
发表于 2009-6-28 23:31:56 | 显示全部楼层
原帖由 wishel 于 2009-6-28 14:24 发表
1,“果直接send失败了handle_time_out会被触发”,是说handle_out吧?
我是这样理解的: 可以写的时候一直写,等到出错返回EWOULDBLOCK,就return。等下次可以写的时候,reactor又会dispatch这个handler,接着写。
2,examples\Reac ...

呵呵,这是我的笔误。应该是send失败之后,下一次reactor会检查到Write_Mask,从而handle_output会被触发。这一点书上和楼主都已经有明确的说明。
你的理解和我是一样的,我的意思是handle_output触发了之后,如果不幸也失败了呢?

是否存在这样一个过程,就是send失败之后,需要把未发送成功的数据保存下来,
大概类似于map<handle,datalist>的东西,在handle_output被触发之后,从这里把未发送出去的数据拿出来。
如果极端情况下,TCP缓冲区持续溢出,handle_output内send又返回-1了,然后handle_output会被连续触发n次,我们手动控制停止。
这样的逻辑是否过于繁琐拖沓?

另,你说的那个例子,作者的意思是,尽管目前这个example证明是有问题的,但是他也不能确认问题就出在ACE类库身上。
因此他希望大家关于这个话题给出好的建议吧。

[ 本帖最后由 modern 于 2009-6-29 00:24 编辑 ]
发表于 2009-6-29 13:00:27 | 显示全部楼层
引用楼主的话:
其次,你需要在handle_input(),和handle_timeout()以及handle_output(),和handle_timeout()中考虑同步,注意是handle_**put与handle_timeout()之间才有同步问题,而handle_input()和handle_output()之间不用考虑。
对比书上的解释:
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.

通过阅读源代码和仔细的看书,发现楼主的话是非常有道理的。
基于TP_Reactor的内部实现,由于同一个ACE_Event_Handler的handle_**put与handle_timeout可能被多个线程同时调用,因此如果有数据共享的话,需要考虑两者之间的同步问题。
而在handle_input和handle_output(),由于均为socket事件,在这里TP_Reactor已经提供了序列化的处理,如果一个线程A获得了该事件的处理权,然后会挂起这个句柄,其他线程在这个线程A处理完之前是无法获得该socket句柄对应的ACE_Event_Handler的处理权的,因此无需考虑同步问题。

P.S.刚开始对书上的理解有歧义,当时以为对于socketIO的序列化需要用户自己去处理,看了源代码之后才发现,其实TP_Reactor已经提供了相当优美的解决方案了,一字之差呀,对介词in的理解不到位~~

另关于可定制化的引用计数的使用方法,楼主已经描述的很清楚了,确实ACE的实现也是非常精彩,这个用法确实3本书中都没有提及,不过确实解决多线程运行TP_Reactor情况下,对于ACE_Event_Handler的生命周期的保护的一个非常好的办法。。

[ 本帖最后由 modern 于 2009-6-29 13:35 编辑 ]
发表于 2009-6-29 14:01:38 | 显示全部楼层
应该是send失败之后,下一次reactor会检查到Write_Mask,从而handle_output会被触发。

我理解的应该不是这样。send失败之后,下一次reactor检查,只有可以send(缓冲区容量够),才会触发。所以handle_output再次触发就不会失败。
这个有点像epoll的et模式,只有状态变化,从不可写到变为可写才会触发。如果一直是可写或不可写状态就不触发。

关于我说的那个例子,我感到奇怪的是这应该不是个太难的问题,这个例子作者能把例子放到ACE的程序包里,应该有渠道和ACE的开发维护团队沟通,为什么不要求他们解决下?这样别人看到这个bug都不敢去用ACE_TP_Reactor 了,起码我不敢用。。。
发表于 2009-6-29 14:34:48 | 显示全部楼层
原帖由 wishel 于 2009-6-29 14:01 发表


我理解的应该不是这样。send失败之后,下一次reactor检查,只有可以send(缓冲区容量够),才会触发。所以handle_output再次触发就不会失败。
这个有点像epoll的et模式,只有状态变化,从不可写到变为可写才会触发。如果一直是可 ...

查了一下msdn你说的是正确的,handle_output再次触发的时候,一定处于可以发送的状态了。
The FD_WRITE network event is handled slightly differently. An FD_WRITE network event is recorded when a socket is first connected with connect/WSAConnect or accepted with accept/WSAAccept, and then after a send fails with WSAEWOULDBLOCK and buffer space becomes available. Therefore, an application can assume that sends are possible starting from the first FD_WRITE network event setting and lasting until a send returns WSAEWOULDBLOCK. After such a failure, the application will find out that sends are again possible when an FD_WRITE network event is recorded and the associated event object is set.
发表于 2009-6-29 15:02:40 | 显示全部楼层
仔细看了一下,你提出的这个例子的问题
如果TP_Reactor真的无法保证the reactor ensures that only one of the handle_*()
methods of an event handler is called at a time,
那么用起来还真是不保准。
用之前至少要需要自己跑一下这个例子,看看实际效果了。
发表于 2009-6-29 15:42:09 | 显示全部楼层
原帖由 modern 于 2009-6-29 15:02 发表
仔细看了一下,你提出的这个例子的问题
如果TP_Reactor真的无法保证the reactor ensures that only one of the handle_*()
methods of an event handler is called at a time,
那么用起来还真是不保准。
用之前至少要 ...

是啊,不过如果它确实按书上所说实现那个协议,leader线程分派handler后就disable相应handle和handler,理论上应该是没问题的。
可能是同步代码哪里有了窗口,破坏了同步。
抽空仔细看看源代码应该能找到问题。
发表于 2009-6-29 17:35:21 | 显示全部楼层
恰好我最近也在阅读这部分的代码,
从实现逻辑上分析,应该是可以按照ACE自己说的那样。

leader线程A分派handle的handler之前,会把dispatch_set_中的handle清掉。
并设置到中suspend_set_中,这也就是所谓的挂起操作了,
这里使用了ACE_Token保证的操作的原子性,
这样在线程A变为跟随者的时候,直到线程A执行完handler,
才会重新把handle从suspend_set_取出,放到wait_set_,
理论上其他线程在整个流程中是没有机会得到handle的handler的。

估计作者自己也比较纳闷为什么会出现bug吧、、、
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-4-29 11:01 , Processed in 0.013048 second(s), 5 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表