一、導(dǎo)讀
在Qt中,常見到三個(gè)exec,第一個(gè)是QApplication::exec(),第二個(gè)是QEventLoop::exec,第三個(gè)是QThread::exec()。本文從源碼角度來看看這三個(gè)exec()。
QApplication::exec()是QApplication類下的一個(gè)靜態(tài)成員函數(shù),該函數(shù)用于進(jìn)入主事件循環(huán)。
QEventLoop::exec是QEventLoop類下的一個(gè)公共成員函數(shù),用于進(jìn)入主事件循環(huán)。
QThread::exec()是QThread類下的一個(gè)受保護(hù)的成員函數(shù),也是用于進(jìn)入事件循環(huán)。
都是進(jìn)入事件循環(huán),他們之間有什么聯(lián)系呢,接著后面的分析。
二、QApplication::exec()
在實(shí)際開發(fā)中,必須調(diào)用QApplication::exec()來啟動(dòng)事件處理,主事件循環(huán)會(huì)從窗口系統(tǒng)接收事件,并將這些事件分派給應(yīng)用程序小部件。在調(diào)用exec()之前不能發(fā)生任何用戶交互,但是存在一種特殊的情況:QMessageBox這樣的模態(tài)小部件可以在調(diào)用exec()之前使用,因?yàn)槟B(tài)小部件會(huì)調(diào)用exec()來啟動(dòng)本地事件循環(huán)。
從源碼角度,QApplication::exec()會(huì)調(diào)用QGuiApplication::exec(),QGuiApplication::exec()會(huì)調(diào)用QCoreApplication::exec():
intQCoreApplication::exec()
{
//檢查exec實(shí)例
if(!QCoreApplicationPrivate::checkInstance("exec"))
return-1;
//獲取線程數(shù)據(jù)QThreadData
QThreadData*threadData=self->d_func()->threadData;
//檢查該函數(shù)的調(diào)用是否在主線程中,如果不是,則返回。
if(threadData!=QThreadData::current()){
qWarning("%s:Mustbecalledfromthemainthread",self->metaObject()->className());
return-1;
}
//檢查是否存在事件循環(huán),如果存在,則返回,否則繼續(xù)后續(xù)操作。
if(!threadData->eventLoops.isEmpty()){
qWarning("QCoreApplication:Theeventloopisalreadyrunning");
return-1;
}
threadData->quitNow=false;
//創(chuàng)建QEventLoop事件循環(huán)對(duì)象
QEventLoopeventLoop;
self->d_func()->in_exec=true;
self->d_func()->aboutToQuitEmitted=false;
//啟動(dòng)事件循環(huán)
intreturnCode=eventLoop.exec();
threadData->quitNow=false;
if(self)
self->d_func()->execCleanup();
returnreturnCode;
}
從上述源碼可見,QApplication的exec()經(jīng)過層層調(diào)用,最終是使用QEventLoop實(shí)現(xiàn)事件循環(huán)。
QApplication::exec()用于啟動(dòng)應(yīng)用程序的事件循環(huán),讓應(yīng)用程序能得以啟動(dòng)運(yùn)行并接收事件。
『備注,執(zhí)行應(yīng)用清理的優(yōu)雅方式』:
建議將清理代碼連接到aboutToQuit()信號(hào),而不是放在應(yīng)用程序的main()函數(shù)中。這是因?yàn)椋谀承┢脚_(tái)上,QApplication::exec()調(diào)用可能不會(huì)返回。例如,在Windows平臺(tái)上,當(dāng)用戶注銷時(shí),系統(tǒng)會(huì)在Qt關(guān)閉所有頂級(jí)窗口后終止該進(jìn)程。因此,不能保證應(yīng)用程序有足夠的時(shí)間退出事件循環(huán),并在QApplication::exec()調(diào)用之后,即在main()函數(shù)的末尾執(zhí)行代碼。
在QCoreApplication::exec()函數(shù)實(shí)現(xiàn)中的這幾行代碼:
if(threadData!=QThreadData::current()){
qWarning("%s:Mustbecalledfromthemainthread",self->metaObject()->className());
return-1;
}
引發(fā)出另一個(gè)有趣的知識(shí)點(diǎn),那就是:在Qt多線程開發(fā)中,需要注意不要阻塞GUI線程,那么哪個(gè)是GUI線程呢?從上述源碼可以明確知道:QApplication a(argc, argv);所在線程就是GUI線程。
三、QThread::exec()
在多線程應(yīng)用設(shè)計(jì)中,QThread::exec()用于為當(dāng)前線程啟動(dòng)一個(gè)新的事件循環(huán),為存在于該線程中的對(duì)象交付事件。在源碼中,QThread::exec()實(shí)現(xiàn)如下:
intQThread::exec()
{
Q_D(QThread);
QMutexLockerlocker(&d->mutex);
d->data->quitNow=false;
if(d->exited){
d->exited=false;
returnd->returnCode;
}
locker.unlock();
//創(chuàng)建QEventLoop事件循環(huán)。
QEventLoopeventLoop;
intreturnCode=eventLoop.exec();
locker.relock();
d->exited=false;
d->returnCode=-1;
returnreturnCode;
}
從源碼角度,也可見QThread::exec()實(shí)現(xiàn)中也調(diào)用到QEventLoop的exec()。
四、QEventLoop::exec()
QEventLoop::exec()用于進(jìn)入主事件循環(huán)并等待直到exit()被調(diào)用。在調(diào)用該函數(shù)時(shí)需要傳入一個(gè)flags,如果指定了標(biāo)志,則只處理標(biāo)志允許的類型的事件,Qt中支持以下幾種標(biāo)志:
| 序號(hào) | 標(biāo)志類型 | 描述 |
|---|---|---|
| 1 | QEventLoop::AllEvents | 所有事件 |
| 2 | QEventLoop::ExcludeUserInputEvents | 不處理用戶輸入事件,例如ButtonPress和KeyPress。 |
| 3 | QEventLoop::ExcludeSocketNotifiers | 不要處理套接字通知事件。 |
| 4 | QEventLoop::WaitForMoreEvents | 如果沒有可用的掛起事件,則等待事件。 |
注意,沒有被傳遞的事件不會(huì)被丟棄,這些事件將在下次傳入不過濾事件的標(biāo)志調(diào)用procesvents()時(shí)被傳遞。
從源碼角度,QEventLoop::exec()實(shí)現(xiàn)如下:
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);
//當(dāng)進(jìn)入一個(gè)新的事件循環(huán)時(shí),刪除已發(fā)布的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()本質(zhì)會(huì)調(diào)用 processEvents()分發(fā)事件。 processEvents()實(shí)現(xiàn)如下:

從上圖所示代碼中,會(huì)調(diào)用QAbstractEventDispatcher::processEvents()實(shí)現(xiàn)事件分發(fā)。QAbstractEventDispatcher類提供了一個(gè)管理Qt事件隊(duì)列的接口,它從窗口系統(tǒng)和其他地方接收事件。然后它將這些事件發(fā)送到QCoreApplication或QApplication實(shí)例進(jìn)行處理和交付。
-
程序
+關(guān)注
關(guān)注
117文章
3846瀏覽量
85258 -
源碼
+關(guān)注
關(guān)注
8文章
685瀏覽量
31332 -
多線程
+關(guān)注
關(guān)注
0文章
279瀏覽量
21039 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4417瀏覽量
67545 -
Qt
+關(guān)注
關(guān)注
2文章
320瀏覽量
40917
原文標(biāo)題:Qt這三個(gè)exec,傻傻分不清!
文章出處:【微信號(hào):嵌入式小生,微信公眾號(hào):嵌入式小生】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
Qt中的三個(gè)exec之間有什么聯(lián)系
評(píng)論