作为信号和槽的得力干将之一,事件循环负责接收和分发事件,它同时支持线程内事件处理和跨线程事件处理,它有一个主事件循环负责维持主进程的持续运行。
目录
Qt程序永生不死的秘密:
通常情况下,Qt应用程序的main函数中都会出现 QApplication和 exec :
int main(int argc, char *argv[])
{
QApplication app(argc, argv);//或者QGuiApplication、QCoreApplication
//any other code
return app.exec();
}
QApplication app(argc, argv);创建并初始化了一个 QApplication 对象,设置一些线程相关的数据。
app.exec();这一步才是真正的开始了一个事件循环,它创建了一个 QEventLoop 对象,正是这个对象处理了程序中Qt的所有事件:
int QCoreApplication::exec()
{
QEventLoop eventLoop;
……
int returnCode = eventLoop.exec();
……
return returnCode;
}
eventLoop.exec()中是一个无限循环,所以程序才不会立即退出,这就是它长生不死的秘密:
int QEventLoop::exec(ProcessEventsFlags flags = AllEvents))
{
Q_D(QEventLoop);
……
while (!d->exit.loadAcquire())
processEvents(flags | WaitForMoreEvents | EventLoopExec);
……
return d->returnCode.load();
}
但是!可是!可但是!最终完成事件分发的却是事件调度器,可以看出,事件调度器调用的是每个线程各自专属的线程调度器,简而言之,就是一群牛马藏在角落替所有人负重前行。。。:
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
if (!d->threadData->hasEventDispatcher())
return false;
return d->threadData->eventDispatcher.load()->processEvents(flags);
}
牛马们
事件分发器已经如此辛劳,我们应当多了解它一下,避免同样成为牛马的结局,每个系统下的牛马都不一样,我们以UNIX的牛马为例:
bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherUNIX);
……
QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
……
timespec *tm = nullptr;
……
d->pollfds.append(d->threadPipe.prepare());
int nevents = 0;
switch (qt_safe_poll(d->pollfds.data(), d->pollfds.size(), tm)) {
……
}
return (nevents > 0);
}
void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
QThreadData *data)
{
……
while (i < data->postEventList.size()) {
……
const QPostEvent &pe = data->postEventList.at(i);
++i;
……
QEvent *e = pe.event;
QObject * r = pe.receiver;
……
QCoreApplication::sendEvent(r, e);
}
……
}
可以看到 sendPostedEvents(QObject *receiver, int event_type,QThreadData *data) 会分发当前队列中阻塞的所有事件。
牛马偷懒的秘诀
当sendPostedEvents执行完之后,如果立即返回并进入下一个事件分发循环,那牛马不就累死了吗?很显然,牛马们并不傻,它们先是通过调用 eventfd 获取了一个文件描述符,并在qt_safe_poll中调用系统底层API,在UNIX中,其调用的是select函数,并把fd给到select,然后当select接收到特定信号后会退出阻塞状态,牛马们接着继续干活。这活儿不一下子就移花接木到系统手里了吗?不得不说,这一招,连笔者都觉得妙啊,不仅不用干活,资源占用还低,被发现摸鱼的概率也极低!
领导视察工作
当领导(QApplication::processEvents())来视察工作的时候,牛马们又会开始奋力干活,没办法,谁家里没有一老一小呢:
void QMyClass::other_func()
{
QApplication::processEvents();//领导
}
//牛马加班
void QApplication::processEvents(QEventLoop::ProcessEventsFlags flags)
{
QThreadData *data = QThreadData::current();
if (!data->hasEventDispatcher())
return;
data->eventDispatcher.load()->processEvents(flags);
}
玩笑归玩笑,QApplication::processEvents() 的作用是立即处理堆积在事件列队中的所有事件,它对我们来说是一个非常有用的函数,当我们不得不在主线程做一些需要长时间等待的任务,而又希望主界面正常使用时,我们就可以每等待10ms执行一次QApplication::processEvents() 确保Qt事件正常分发,主界面刷新不会卡死。