加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

QEventLoop 销毁时注意事项

(2011-01-02 13:44:03)
标签:

qeventloop

qt

源码

venus

it

分类: It文章
查看原文:http://newfaction.net/2011/01/02/qeventloop-destroyed-notes.html
上篇文章对QT的QEventLoop 做了简单介绍, 通过对QEventLoop 源码的查阅,QEventLoop的实现无非是: 线程+PostEvent的强化。 有用到QEventLoop的童鞋可能会发现 "为什么QEventLoop在调用exit()以后,直接调用delete 销毁QEventLoop时将出现异常!!!下面做简单剖析:

上篇文章中,关于QEventLoop::processEvents的实现:

  bool QEventLoop::processEvents( ProcessEventsFlags flags )
 {
  // process events from the QWS server
  int nevents = 0;
 ......

  // handle gui and posted events
  if (qt_is_gui_used ) {
 //a.首先执行globalPostedEvents队列中的事件,这些事件都是通过QApplication::postEvent或QThread::postEvent发送的
 QApplication::sendPostedEvents();

 //b.检查QWSEvent队列:QApplication::Data的QPtrList<QWSEvent> queue队列
 while ( qt_fbdpy->eventPending() ) { // also flushes output buffer
  if ( d->shortcut ) {
  return FALSE;
  }

  QWSEvent *event = qt_fbdpy->getEvent(); // get next event
  nevents++;

  bool ret = qApp->qwsProcessEvent( event ) == 1;
  delete event;
  if ( ret ) {
  return TRUE;
  }
 }
  }

  if ( d->shortcut ) {
 return FALSE;
  }

 //c.如果是QWSServer,需要检查QWSServer的QPtrList<QWSCommandStruct> commandQueue队列。
  extern QPtrQueue<QWSCommand> *qt_get_server_queue();
  if ( !qt_get_server_queue()->isEmpty() ) {
 QWSServer::processEventQueue();
  }

 //d.如果上面的事件执行过程中产生postedEvent,再次执行,主是要一些moveEvent,resizeEvent,paintEvent
  QApplication::sendPostedEvents();

  // don't block if exitLoop() or exit()/quit() has been called.
  bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore);

  // Process timers and socket notifiers - the common UNIX stuff

 //e.检查timer事件队列中,确发时间最小的一个,确定为select的等待时间。
  // return the maximum time we can wait for an event.
  static timeval zerotm;
  timeval *tm = qt_wait_timer(); // wait for timer or event
  if ( !canWait ) {
 if ( !tm )
  tm = &zerotm;
 tm->tv_sec = 0; // no time to wait
 tm->tv_usec = 0;
  }

 //f.准备select调用所需要的fd队列。
  int highest = 0;
  if ( ! ( flags & ExcludeSocketNotifiers ) ) {
 // return the highest fd we can wait for input on
 if ( d->sn_highest >= 0 ) { // has socket notifier(s)
  if ( d->sn_vec[0].list && ! d->sn_vec[0].list->isEmpty() )
  d->sn_vec[0].select_fds = d->sn_vec[0].enabled_fds;
  else
  FD_ZERO( &d->sn_vec[0].select_fds );

  if ( d->sn_vec[1].list && ! d->sn_vec[1].list->isEmpty() )
  d->sn_vec[1].select_fds = d->sn_vec[1].enabled_fds;
  else
  FD_ZERO( &d->sn_vec[1].select_fds );

  if ( d->sn_vec[2].list && ! d->sn_vec[2].list->isEmpty() )
  d->sn_vec[2].select_fds = d->sn_vec[2].enabled_fds;
  else
  FD_ZERO( &d->sn_vec[2].select_fds );
 }


 //g.检查有没有注册的preselect_handler
  if ( qt_preselect_handler ) {
 QVFuncList::Iterator it, end = qt_preselect_handler->end();
 for ( it = qt_preselect_handler->begin(); it != end; ++it )
  (**it)();
  }

 //h,执行select调用。
 ...................
 nsel = select( highest + 1,
  &d->sn_vec[0].select_fds,
  &d->sn_vec[1].select_fds,
  &d->sn_vec[2].select_fds,
  tm );

 ...............

 //i.看看是否有thread发来的唤醒消息
  // some other thread woke us up... consume the data on the thread pipe so that
  // select doesn't immediately return next time
  if ( nsel > 0 && FD_ISSET( d->thread_pipe[0], &d->sn_vec[0].select_fds ) ) {
 char c;
 ::read( d->thread_pipe[0], &c, 1 );
  }

 //j.检查是否有注册的postselect_hander事件
  if ( qt_postselect_handler ) {
 QVFuncList::Iterator it, end = qt_postselect_handler->end();
 for ( it = qt_postselect_handler->begin(); it != end; ++it )
  (**it)();
  }

 //k.检查所有能进行操作的socket fd,并执行相应的QSocketNotifier::activated()

  // activate socket notifiers
 .....................
 nevents += activateSocketNotifiers();

 //l.检查timerList是否有timeout的timer并执行.
  // activate timers
  nevents += activateTimers();

  // return true if we handled events, false otherwise
  return (nevents > 0);
 }
从源码中可以发现QEventLoop是由一个线程在后台间隔的调用processEvent的过程,在调用exit()时,做的工作如下:
1、设置exit状态标志
2、发出中断信号
第一步直接效果于: 调用isRunning等时,直接返回false; 第二步中断信号:是修改线程中某个标志位,让线程下次轮循调用到processEvent前退出工作流程! 因而调用exit() 并不能立刻使eventLoop退出,需要等待片刻,而这个片刻的时间大概多长呢? 答案是“一个processEvent”的工作周期。

如何让QEventLoop正常的工作

请把握以下几点: 1、调用exit后让QEventLoop 调用deleteLater()销毁, 或者调用定时器销毁 2、调用QEventLoop exec()方法的类不能优先于QEventLoop 销毁~! 有效把握这两点,QEventLoop 就任由你掌控~

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有