一、导读
在Qt中,常见到三个exec,第一个是QApplication::exec(),第二个是QEventLoop::exec,第三个是QThread::exec()。本文从源码角度来看看这三个exec()。
QApplication::exec()是QApplication类下的一个静态成员函数,该函数用于进入主事件循环。
QEventLoop::exec是QEventLoop类下的一个公共成员函数,用于进入主事件循环。
QThread::exec()是QThread类下的一个受保护的成员函数,也是用于进入事件循环。
都是进入事件循环,他们之间有什么联系呢,接着后面的分析。
二、QApplication::exec()
在实际开发中,必须调用QApplication::exec()来启动事件处理,主事件循环会从窗口系统接收事件,并将这些事件分派给应用程序小部件。在调用exec()之前不能发生任何用户交互,但是存在一种特殊的情况:QMessageBox这样的模态小部件可以在调用exec()之前使用,因为模态小部件会调用exec()来启动本地事件循环。
从源码角度,QApplication::exec()会调用QGuiApplication::exec(),QGuiApplication::exec()会调用QCoreApplication::exec():
intQCoreApplication::exec() { //检查exec实例 if(!QCoreApplicationPrivate::checkInstance("exec")) return-1; //获取线程数据QThreadData QThreadData*threadData=self->d_func()->threadData; //检查该函数的调用是否在主线程中,如果不是,则返回。 if(threadData!=QThreadData::current()){ qWarning("%s:Mustbecalledfromthemainthread",self->metaObject()->className()); return-1; } //检查是否存在事件循环,如果存在,则返回,否则继续后续操作。 if(!threadData->eventLoops.isEmpty()){ qWarning("QCoreApplication:Theeventloopisalreadyrunning"); return-1; } threadData->quitNow=false; //创建QEventLoop事件循环对象 QEventLoopeventLoop; self->d_func()->in_exec=true; self->d_func()->aboutToQuitEmitted=false; //启动事件循环 intreturnCode=eventLoop.exec(); threadData->quitNow=false; if(self) self->d_func()->execCleanup(); returnreturnCode; }
从上述源码可见,QApplication的exec()经过层层调用,最终是使用QEventLoop实现事件循环。
QApplication::exec()用于启动应用程序的事件循环,让应用程序能得以启动运行并接收事件。
『备注,执行应用清理的优雅方式』:
建议将清理代码连接到aboutToQuit()信号,而不是放在应用程序的main()函数中。这是因为,在某些平台上,QApplication::exec()调用可能不会返回。例如,在Windows平台上,当用户注销时,系统会在Qt关闭所有顶级窗口后终止该进程。因此,不能保证应用程序有足够的时间退出事件循环,并在QApplication::exec()调用之后,即在main()函数的末尾执行代码。
在QCoreApplication::exec()函数实现中的这几行代码:
if(threadData!=QThreadData::current()){ qWarning("%s:Mustbecalledfromthemainthread",self->metaObject()->className()); return-1; }
引发出另一个有趣的知识点,那就是:在Qt多线程开发中,需要注意不要阻塞GUI线程,那么哪个是GUI线程呢?从上述源码可以明确知道:QApplication a(argc, argv);所在线程就是GUI线程。
三、QThread::exec()
在多线程应用设计中,QThread::exec()用于为当前线程启动一个新的事件循环,为存在于该线程中的对象交付事件。在源码中,QThread::exec()实现如下:
intQThread::exec() { Q_D(QThread); QMutexLockerlocker(&d->mutex); d->data->quitNow=false; if(d->exited){ d->exited=false; returnd->returnCode; } locker.unlock(); //创建QEventLoop事件循环。 QEventLoopeventLoop; intreturnCode=eventLoop.exec(); locker.relock(); d->exited=false; d->returnCode=-1; returnreturnCode; }
从源码角度,也可见QThread::exec()实现中也调用到QEventLoop的exec()。
四、QEventLoop::exec()
QEventLoop::exec()用于进入主事件循环并等待直到exit()被调用。在调用该函数时需要传入一个flags,如果指定了标志,则只处理标志允许的类型的事件,Qt中支持以下几种标志:
序号 | 标志类型 | 描述 |
---|---|---|
1 | QEventLoop::AllEvents | 所有事件 |
2 | QEventLoop::ExcludeUserInputEvents | 不处理用户输入事件,例如ButtonPress和KeyPress。 |
3 | QEventLoop::ExcludeSocketNotifiers | 不要处理套接字通知事件。 |
4 | QEventLoop::WaitForMoreEvents | 如果没有可用的挂起事件,则等待事件。 |
注意,没有被传递的事件不会被丢弃,这些事件将在下次传入不过滤事件的标志调用procesvents()时被传递。
从源码角度,QEventLoop::exec()实现如下:
intQEventLoop::exec(ProcessEventsFlagsflags) { Q_D(QEventLoop); autothreadData=d->threadData.loadRelaxed(); //weneedtoprotectfromraceconditionwithQThread::exit QMutexLockerlocker(&static_cast(QObjectPrivate::get(threadData->thread.loadAcquire()))->mutex); if(threadData->quitNow) return-1; if(d->inExec){ qWarning("QEventLoop:instance%phasalreadycalledexec()",this); return-1; } structLoopReference{ QEventLoopPrivate*d; QMutexLocker&locker; boolexceptionCaught; LoopReference(QEventLoopPrivate*d,QMutexLocker&locker):d(d),locker(locker),exceptionCaught(true) { d->inExec=true; d->exit.storeRelease(false); autothreadData=d->threadData.loadRelaxed(); ++threadData->loopLevel; threadData->eventLoops.push(d->q_func()); locker.unlock(); } ~LoopReference() { if(exceptionCaught){ qWarning("Qthascaughtanexceptionthrownfromaneventhandler.Throwing " "exceptionsfromaneventhandlerisnotsupportedinQt. " "YoumustnotletanyexceptionwhatsoeverpropagatethroughQtcode. " "Ifthatisnotpossible,inQt5youmustatleastreimplement " "QCoreApplication::notify()andcatchallexceptionsthere. "); } locker.relock(); autothreadData=d->threadData.loadRelaxed(); QEventLoop*eventLoop=threadData->eventLoops.pop(); Q_ASSERT_X(eventLoop==d->q_func(),"QEventLoop::exec()","internalerror"); Q_UNUSED(eventLoop);//--releasewarning d->inExec=false; --threadData->loopLevel; } }; LoopReferenceref(d,locker); //当进入一个新的事件循环时,删除已发布的exit事件 QCoreApplication*app=QCoreApplication::instance(); if(app&&app->thread()==thread()) QCoreApplication::removePostedEvents(app,QEvent::Quit); #ifdefQ_OS_WASM //Partialsupportfornestedeventloops:MaketheruntimethrowaJavaSrcript //exception,whichreturnscontroltothebrowserwhilepreservingtheC++stack. //Eventprocessingthencontinuesasnormal.Thesleepcallbelowneverreturns. //QTBUG-70185 if(threadData->loopLevel>1) emscripten_sleep(1); #endif while(!d->exit.loadAcquire()) processEvents(flags|WaitForMoreEvents|EventLoopExec); ref.exceptionCaught=false; returnd->returnCode.loadRelaxed(); }
从上述源码可知,QEventLoop::exec()本质会调用 processEvents()分发事件。 processEvents()实现如下:
从上图所示代码中,会调用QAbstractEventDispatcher::processEvents()实现事件分发。QAbstractEventDispatcher类提供了一个管理Qt事件队列的接口,它从窗口系统和其他地方接收事件。然后它将这些事件发送到QCoreApplication或QApplication实例进行处理和交付。
-
程序
+关注
关注
116文章
3754浏览量
80724 -
源码
+关注
关注
8文章
632浏览量
29100 -
多线程
+关注
关注
0文章
277浏览量
19891 -
函数
+关注
关注
3文章
4276浏览量
62303 -
Qt
+关注
关注
1文章
301浏览量
37774
原文标题:Qt这三个exec,傻傻分不清!
文章出处:【微信号:嵌入式小生,微信公众号:嵌入式小生】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论