ACE反应器框架引用计数策略的使用

    技术2022-05-11  33

    先前遇到一个问题,程序总是crash,最后发现原来是没有使用反应器框架的引用计数策略造成的.现将这个问题以及解决方法总结如下: 当时遇到的情况是,在handle_output的send_n时有时候会出现Timer expired(长时间断开网络会出现这种情况的错误,导致程序异常退出. // 源码如下 int  gkClient::handle_output (ACE_HANDLE) {         TRACE(ACE_TEXT("gkClient::handle_output"));         ACE_Message_Block *mb;         ACE_Time_Value nowait (ACE_OS::gettimeofday ());         ACE_Time_Value wait;         wait.sec(5);         while (-1 != this->msg_queue()->dequeue_prio(mb, &nowait))         {                 ssize_t send_cnt = this->peer ().send_n (mb->rd_ptr (), mb->length (),&wait);                 ACE_DEBUG((LM_DEBUG,ACE_TEXT("%I(%D)(%P|%t) mb length:%d! "),mb- >length()));                 mb->release ();                 if (send_cnt <=0)                 {                         ACE_DEBUG((LM_DEBUG,ACE_TEXT("%I(%D)(%P|%t) send msg to SocketServer failed! ")));                         ACE_ERROR((LM_ERROR,ACE_TEXT("%I(%D)(%P|%t) %p "),ACE_TEXT ("send")));                         //remove WRITE_MASK                         this->reactor()->cancel_wakeup(this,ACE_Event_Handler::WRITE_MASK);                         this->msg_queue ()->notification_strategy (0);                         //call back handle_close                         TRACE_RETURN(-1);                 }     }     TRACE_RETURN(0);} 应该说,在send失败的时候就cancle掉了WRITE_MASK,这样应该就不会再回调handle_output,可是日志上 显示此后又调用了一次handle_output还是Timer experid的错误,此时程序就掉了. 程序crash时的日志信息:    (Wed Jul   4   2007   12 : 01 : 43.997264 )( 4195 | 49544096 ) mb length: 495 ! (Wed Jul   4   2007   12 : 01 : 43.997502 )( 4195 | 49544096 ) send msg to SocketServer failed ! (Wed Jul   4   2007   12 : 01 : 43.997617 )( 4195 | 49544096 ) send: Timer expired(Wed Jul   4   2007   12 : 01 : 43.998079 )handle_close !                                              // 回调handle_close的日志 (Wed Jul   4   2007   12 : 01 : 43.998272 )handle_close over !                                     // 回调handle_close的日志 (Wed Jul   4   2007   12 : 01 : 43.998383 ) ~ gkClient !                                                               // 析构 (Wed Jul   4   2007   12 : 01 : 43.998494 )SocketServer  192.168 . 200.88 : 2196  handle_close !            (Wed Jul   4   2007   12 : 01 : 43.999373 ) ~ gkClient over !                                                       // 析构完毕 (Wed Jul   4   2007   12 : 01 : 44.007225 )( 4195 | 126053280 ) mb length:  495 !                         // 又一次发送 (Wed Jul   4   2007   12 : 01 : 44.007411 )( 4195 | 126053280 ) send msg to SocketServer failed ! (Wed Jul   4   2007   12 : 01 : 44.007516 )( 4195 | 126053280 ) send: Timer expired                 // 到这里程序掉了 由日志可以看出两次发送是有两个线程(49544096和126053280)分别调用的,现在看来这两个线程间应该是并发 执行的.不知道ACE这一部分是怎么控制实现并发的,谁知道的可以介绍一下:) 为什么会掉呢? 看了下面这段话你就知道了,本人翻译的不好,所以还是大家自己看吧. the various reactor implementations vary to some degree in their dispatching and synchronization behavior in the presence of multiple threads. It's a relatively simple matter (as far as simple goes when considering synchronization, that is) to serialize access across the standard I/O callbacks (handle_input, handle_output, and handle_exception). However, when it comes to knowing when it's safe to delete an event handler object, it gets harder to ensure a safe deletion without deleting the object out from under a concurrent callback. To effectively address these situations, ACE 5.4 and later implements a reference counting scheme in the Reactor framework. It effectively maintains a reference count that's incremented on each operation (such as initial handler registration and each dispatch operation) and decremented when complete (final handler unregistration and dispatch completion). When the reference count goes to 0, the Reactor framework deletes the event handler. This scheme ensures that an event handler is deleted only when it is not referenced any longer. To preserve pre-ACE-5.4 functionality, the Reactor framework's event handler reference counting functionality is disabled by default. It must be enabled on each ACE_Event_Handler object individually, prior to its use in any Reactor method call. To enable reference counting on a handler, add code like this to its constructor or other initialization point, before the object is used in any reactor method calls: // Enable reference counting. this->reference_counting_policy ().value (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); That is all you need. Be sure you don't delete the handler yourself, unless you need to before registering it with the reactor. Once registered for reactor event handling, the reactor will take care of deleting the handler when there are no reactor registrations and no more callbacks in progress. 原文链接:http://www.riverace.com/newsletters/October2005.htm 我的理解就是,ACE的反应器框架在ACE_Event_Handler中使用了引用计数策略来保证event handler在并发调用的情况下能够被安全的删除.但是默认的情况下引用计数策略没有启用,所以如果你的程序中并发的调用的event handler,应该启用引用计数策略. 将下行代码加到构造函数里即可启用引用计数策略: this->reference_counting_policy ().value(ACE_Event_Handler::Reference_Counting_Policy::ENABLED);

    最新回复(0)