ACE_Reactor(四):ACE_WFMO_Reactor

实现接口不同于类Unix平台上,select在windows上仅仅支持socket句柄的多路分离。而且在Unix平台上,select也不支持同步对象、线程或者SystemV消息队列的多路分离。
所以windows上增加了以WaitForMultipleObjets系统函数替代select的ACE_WFMO_Reactor类。
其新增特性有:
1.因为WaitForMultipleObjets支持多线程同时并发调用,所以不再需要实现owner()函数来进行序列化来按照Leader/Followers的方式轮流进行分发事件。
2.新增了和select相似的对应的Handler_Repository和Reactor_Notify的类,用于处理IO及定时器事件。
3.每一个对handle_events的调用都会等待一个句柄变成活动句柄。但是在获取event时,会遍历获取所有的活动句柄避免其他活动句柄被饿死。
4.ACE_WFMO_Reactor的子类,应用程序能处理所有挂载上的事件以及窗口消息。
ACE_WFMO_Reactor是ACE_Reactor在windows上的默认实现。

00848 ACE_INLINE int
00849 ACE_WFMO_Reactor::handle_events (ACE_Time_Value &how_long)
00850 {
00851   return this->event_handling (&how_long,FALSE);
00852 }

event_handling()函数中存在的dowhile循环中会调用wait_for_multiple_events,这个函数是只要有任意句柄触发就会返回,然后会去调用safe_dispatch函数去分发,这个函数中直接调用了dispatch。而dispatch中会调用dispatch_handles函数,这个函数在说明就直说了“
Dispatches any active handles from handles_[] to handles_[active_handles_] using to poll through our handle set looking for active handles.”。即这个函数中不仅会分发要取的这一个活动句柄,还会把所有的句柄集中的所有的活动句柄都获取到。
5.事件分发时会出现,指向相同处理器handler的多线程化分派。
可能会出现线程间的竞争状态。比如线程一上socket正在处理IO事件,另一个IO事件又被其他线程分发处理了。所以需要显式的对竞争状态进行保护。

01954 int
01955 ACE_WFMO_Reactor::dispatch_handler (DWORD slot,01956                                     DWORD max_handlep1)
01957 {
01958   // Check if there are window messages that need to be dispatched
01959   if (slot == max_handlep1)
01960     return this->dispatch_window_messages ();
01961 
01962   // Dispatch the handler if it has not been scheduled for deletion.
01963   // Note that this is a very week test if there are multiple threads
01964   // dispatching this slot as no locks are held here. Generally,you
01965   // do not want to do something like deleting the this pointer in
01966   // handle_close() if you have registered multiple times and there is
01967   // more than one thread in WFMO_Reactor->handle_events().
01968   else if (!this->handler_rep_.scheduled_for_deletion (slot))
01969     {
01970       ACE_HANDLE event_handle = *(this->handler_rep_.handles () + slot);
01971 
01972       if (this->handler_rep_.current_info ()[slot].io_entry_)
01973         return this->complex_dispatch_handler (slot,01974                                                event_handle);
01975       else
01976         return this->simple_dispatch_handler (slot,01977                                               event_handle);
01978     }
01979   else
01980     // The handle was scheduled for deletion,so we will skip it.
01981     return 0;
01982 }
01983

上述代码是实际分派时,而看到的对于竞争状态的避免,则在如下代码中:

02020 int
02021 ACE_WFMO_Reactor::complex_dispatch_handler (DWORD slot,02022                                             ACE_HANDLE event_handle)
02023 {
02024   // This dispatch is used for I/O entires.
02025 
02026   ACE_WFMO_Reactor_Handler_Repository::Current_Info &current_info =
02027     this->handler_rep_.current_info ()[slot];
02028 
02029   WSANETWORKEVENTS events;
02030   ACE_Reactor_Mask problems = ACE_Event_Handler::NULL_MASK;
02031   if (::WSAEnumNetworkEvents ((SOCKET) current_info.io_handle_,02032                               event_handle,02033                               &events) == SOCKET_ERROR)
02034     problems = ACE_Event_Handler::ALL_EVENTS_MASK;
02035   else
02036     {
02037       // Prepare for upcalls. Clear the bits from <events> representing
02038       // events the handler is not interested in. If there are any left,
02039       // do the upcall(s). upcall will replace events.lNetworkEvents
02040       // with bits representing any functions that requested a repeat
02041       // callback before checking handles again. In this case,continue
02042       // to call back unless the handler is unregistered as a result of
02043       // one of the upcalls. The way this is written,the upcalls will
02044       // keep being done even if one or more upcalls reported problems.
02045       // In practice this may turn out not so good,but let's see. If any
02046       // problems,please notify Steve Huston <shuston@riverace.com>
02047       // before or after you change this code.
02048       events.lNetworkEvents &= current_info.network_events_;
02049       while (events.lNetworkEvents != 0)
02050         {
02051           ACE_Event_Handler *event_handler =
02052             current_info.event_handler_;
02053 
02054           int reference_counting_required =
02055             event_handler->reference_counting_policy ().value () ==
02056             ACE_Event_Handler::Reference_Counting_Policy::ENABLED;
02057 
02058           // Call add_reference() if needed.
02059           if (reference_counting_required)
02060             {
02061               event_handler->add_reference ();
02062             }
02063 
02064           // Upcall
02065           problems |= this->upcall (current_info.event_handler_,02066                                     current_info.io_handle_,02067                                     events);
02068 
02069           // Call remove_reference() if needed.
02070           if (reference_counting_required)
02071             {
02072               event_handler->remove_reference ();
02073             }
02074 
02075           if (this->handler_rep_.scheduled_for_deletion (slot))
02076             break;
02077         }
02078     }
02079 
02080   if (problems != ACE_Event_Handler::NULL_MASK
02081       && !this->handler_rep_.scheduled_for_deletion (slot)  )
02082     this->handler_rep_.unbind (event_handle,problems);
02083 
02084   return 0;
02085 }

须知这里的 events.lNetworkEvents是直接从WSAEnumNetworkEvents中取出来的。用MSDN的原话”Pointe t a WSANETWORKEVENTS structure that i s filled with a recod of network events that occurred and an associated error codes.”在具体的upcall执行时比TP_Select或者Select_T多了很多关于mask的判断。

02087 ACE_Reactor_Mask
02088 ACE_WFMO_Reactor::upcall (ACE_Event_Handler *event_handler,02089                           ACE_HANDLE io_handle,02090                           WSANETWORKEVENTS &events)
02091 {
02092   // This method figures out what exactly has happened to the socket
02093   // and then calls appropriate methods.
02094   ACE_Reactor_Mask problems = ACE_Event_Handler::NULL_MASK;
02095 
02096   // Go through the events and do the indicated upcalls. If the handler
02097   // doesn't want to be called back,clear the bit for that event.
02098   // At the end,set the bits back to <events> to request a repeat call.
02099 
02100   long actual_events = events.lNetworkEvents;
02101   int action;
02102 
02103   if (ACE_BIT_ENABLED (actual_events,FD_WRITE))
02104     {
02105       action = event_handler->handle_output (io_handle);
02106       if (action <= 0)
02107         {
02108           ACE_CLR_BITS (actual_events,FD_WRITE);
02109           if (action == -1)
02110             ACE_SET_BITS (problems,ACE_Event_Handler::WRITE_MASK);
02111         }
02112     }

需要注意的地方是:
1.waitForMultipleObjects的WRITE_MASK的语义和select不同!如果接受不了,还是切回到ACE_Select_Reactor吧。select可以持续的检测一个socket的可写状态,只要可以就可以一直被检测到。而waitForMultipleObjects则只在第一次被连接时设定WRITE事件,当检测到写事件后就只能一直不停的写或者send直至失败或者返回EWOULDBLOCK.
2.ACE_WFMO_Reactor不再需要在处理事件前,将句柄挂起,然后处理完再恢复。因为当ACE_WFMO_Reactor线程处理事件需要从WSAEnumNetworkEvents()获取该IO事件的掩码时,os会自动清除这个额句柄的内部掩码,最终结果是即使多个线程同时多路分离socket句柄,也只能有一个线程获取IO事件掩码并将分配处理器。
而select没有这样的自动关OS序列化的能力,所以才有挂起和恢复操作。

注: WSAEventSelect是用来将其他事件对象等和socket进行绑定的函数,然后绑定的socket被用于检测,若socket检测到事件则说明绑定的对象上有事件了。 WSAEnumNetworkEvents用来获取激活的事件IO事件的掩码用于处理。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom