Threads and QObjects
Threads 繼承了 QObjects類(lèi)。它發(fā)出信號(hào)來(lái)指示線(xiàn)程已開(kāi)始執(zhí)行或已完成執(zhí)行,并提供一些槽函數(shù)。
更有趣的是,QObjects可以在多個(gè)線(xiàn)程中使用,發(fā)出調(diào)用其他線(xiàn)程中槽的信號(hào),并將事件發(fā)布到“l(fā)ive”在其他線(xiàn)程中的對(duì)象。這是可能的,因?yàn)槊總€(gè)線(xiàn)程都允許有自己的事件循環(huán)。
QObject Reentrancy QObject的重入
QObject是可重入的。它的大多數(shù)非GUI子類(lèi),如QTimer、QTcpSocket、QUdpSocket和QProcess,也是可重入的,因此可以同時(shí)從多個(gè)線(xiàn)程使用這些類(lèi)。請(qǐng)注意,這些類(lèi)被設(shè)計(jì)為在單個(gè)線(xiàn)程中創(chuàng)建和使用;不能保證在一個(gè)線(xiàn)程中創(chuàng)建對(duì)象并從另一個(gè)線(xiàn)程調(diào)用其函數(shù)。有三個(gè)限制需要注意:
1、QObject的子對(duì)象必須始終在創(chuàng)建父對(duì)象的線(xiàn)程中創(chuàng)建。這意味著,除其他外,永遠(yuǎn)不應(yīng)該將QThread對(duì)象(This)作為在線(xiàn)程中創(chuàng)建的對(duì)象的父對(duì)象傳遞(因?yàn)镼Thread物體本身是在另一個(gè)線(xiàn)程中創(chuàng)建)。
2、事件驅(qū)動(dòng)的對(duì)象只能在單個(gè)線(xiàn)程中使用。具體地,這適用于定時(shí)器機(jī)制和網(wǎng)絡(luò)模塊。例如,不能在不是對(duì)象線(xiàn)程的線(xiàn)程中啟動(dòng)計(jì)時(shí)器或連接套接字。
3、刪除QThread之前,必須確保刪除在線(xiàn)程中創(chuàng)建的所有對(duì)象。這可以通過(guò)在run()實(shí)現(xiàn)中的堆棧上創(chuàng)建對(duì)象來(lái)輕松完成。
盡管QObject是可重入的,但GUI類(lèi),尤其是QWidget及其所有子類(lèi),是不可重入。它們只能從主線(xiàn)程使用。如前所述,還必須從該線(xiàn)程調(diào)用QCoreApplication::exec()。在實(shí)踐中,通過(guò)將耗時(shí)的操作放在單獨(dú)的工作線(xiàn)程中,并在工作線(xiàn)程完成時(shí)在主線(xiàn)程的屏幕上顯示結(jié)果,可以很容易地解決在主線(xiàn)程以外的其他線(xiàn)程中使用GUI類(lèi)的不可能性。這是用于實(shí)現(xiàn)Mandelbrot示例
和Blocking Fortune客戶(hù)端示例
的方法。
通常,不支持在QApplication之前創(chuàng)建QObjects,這可能會(huì)導(dǎo)致退出時(shí)出現(xiàn)奇怪的崩潰,具體取決于平臺(tái)。這意味著也不支持QObject的靜態(tài)實(shí)例。一個(gè)結(jié)構(gòu)合理的單線(xiàn)程或多線(xiàn)程應(yīng)用程序應(yīng)該使QApplication成為第一個(gè)創(chuàng)建的和最后一個(gè)銷(xiāo)毀的QObject。
Per-Thread Event Loop 每線(xiàn)程事件循環(huán)
每個(gè)線(xiàn)程都可以有自己的事件循環(huán)。初始線(xiàn)程使用QCoreApplication::exec()啟動(dòng)其事件循環(huán),或者對(duì)于單對(duì)話(huà)框GUI應(yīng)用程序,有時(shí)使用QDialog::exec()。其他線(xiàn)程可以使用QThread::exec()啟動(dòng)事件循環(huán)。與QCoreApplication一樣,QThread提供了一個(gè)exit(int)函數(shù)和一個(gè)quit()槽。
線(xiàn)程中的事件循環(huán)使線(xiàn)程可以使用某些需要事件循環(huán)的非GUI Qt類(lèi)(如QTimer、QTcpSocket和QProcess)。它還可以將來(lái)自任何線(xiàn)程的信號(hào)連接到特定線(xiàn)程的插槽。這將在下面的“跨線(xiàn)程的信號(hào)和插槽”
部分中進(jìn)行更詳細(xì)的解釋。
QObject實(shí)例被認(rèn)為存在于創(chuàng)建它的線(xiàn)程中。到該對(duì)象的事件由該線(xiàn)程的事件循環(huán)調(diào)度。使用QObject::thread()可以獲得QObject所在的線(xiàn)程。
QObject::moveToThread()函數(shù)更改對(duì)象及其子對(duì)象的線(xiàn)程相關(guān)性(如果對(duì)象有父對(duì)象,則不能移動(dòng)該對(duì)象)。
從擁有QObject的線(xiàn)程以外的線(xiàn)程調(diào)用delete(或以其他方式訪(fǎng)問(wèn)該對(duì)象)是不安全的,除非您保證該對(duì)象當(dāng)時(shí)沒(méi)有處理事件。改為使用QObject::deleteLater(),將發(fā)布一個(gè)DeferredDelete事件,對(duì)象線(xiàn)程的事件循環(huán)最終將接收該事件。默認(rèn)情況下,擁有QObject的線(xiàn)程是創(chuàng)建QOObject的線(xiàn)程,但不是在調(diào)用QObject::moveToThread()之后。
如果沒(méi)有事件循環(huán)正在運(yùn)行,則不會(huì)將事件傳遞到對(duì)象。例如,如果在線(xiàn)程中創(chuàng)建一個(gè)QTimer對(duì)象,但從不調(diào)用exec(),則QTimer將永遠(yuǎn)不會(huì)發(fā)出其timeout()信號(hào)。調(diào)用deleteLater()也不起作用。(這些限制也適用于主線(xiàn)程。)
您可以使用線(xiàn)程安全函數(shù)QCoreApplication::postEvent()隨時(shí)手動(dòng)將事件發(fā)布到任何線(xiàn)程中的任何對(duì)象。事件將由創(chuàng)建對(duì)象的線(xiàn)程的事件循環(huán)自動(dòng)調(diào)度。
所有線(xiàn)程都支持事件篩選器,但有一個(gè)限制,即監(jiān)視對(duì)象必須與被監(jiān)視對(duì)象位于同一線(xiàn)程中。類(lèi)似地,QCoreApplication::sendEvent()(與postEvent()不同)只能用于將事件調(diào)度到調(diào)用函數(shù)的線(xiàn)程中的對(duì)象。
Accessing QObject Subclasses from Other Threads從其他線(xiàn)程訪(fǎng)問(wèn)QObject子類(lèi)
QObject及其所有子類(lèi)都不是線(xiàn)程安全的。這包括整個(gè)事件傳遞系統(tǒng)。重要的是要記住,當(dāng)您從另一個(gè)線(xiàn)程訪(fǎng)問(wèn)對(duì)象時(shí),事件循環(huán)可能會(huì)將事件傳遞到您的QObject子類(lèi)。
如果您正在調(diào)用一個(gè)不在當(dāng)前線(xiàn)程中的QObject子類(lèi)上的函數(shù),并且該對(duì)象可能會(huì)接收事件,則必須使用互斥鎖保護(hù)對(duì)QObject個(gè)子類(lèi)內(nèi)部數(shù)據(jù)的所有訪(fǎng)問(wèn);否則,您可能會(huì)遇到崩潰或其他不希望的行為。
與其他對(duì)象一樣,QThread對(duì)象位于創(chuàng)建對(duì)象的線(xiàn)程中,而不是在調(diào)用QThread::run()時(shí)創(chuàng)建的線(xiàn)程中。在QThread子類(lèi)中提供槽通常是不安全的,除非使用互斥體保護(hù)成員變量。
另一方面,您可以安全地從QThread::run()實(shí)現(xiàn)中發(fā)出信號(hào),因?yàn)樾盘?hào)發(fā)射是線(xiàn)程安全的。
Signals and Slots Across Threads跨線(xiàn)程的信號(hào)和插槽
Qt支持以下信號(hào)槽連接類(lèi)型:
自動(dòng)連接(默認(rèn))如果信號(hào)是在接收對(duì)象具有關(guān)聯(lián)性的線(xiàn)程中發(fā)出的,則行為與直接連接相同。否則,行為與排隊(duì)連接相同。
直接連接發(fā)出信號(hào)時(shí),會(huì)立即調(diào)用槽。槽在發(fā)射器的線(xiàn)程中執(zhí)行,而發(fā)射器的線(xiàn)程不一定是接收器的線(xiàn)程。
排隊(duì)連接當(dāng)控制返回到接收器線(xiàn)程的事件循環(huán)時(shí),將調(diào)用槽。槽在接收器的線(xiàn)程中執(zhí)行。
阻塞排隊(duì)連接調(diào)用槽時(shí)與調(diào)用排隊(duì)連接時(shí)一樣,但當(dāng)前線(xiàn)程會(huì)阻塞,直到槽返回為止。注意:使用此類(lèi)型連接同一線(xiàn)程中的對(duì)象將導(dǎo)致死鎖。
唯一連接此行為與自動(dòng)連接相同,但只有在不復(fù)制現(xiàn)有連接的情況下才能建立連接。即,如果同一信號(hào)已經(jīng)連接到同一對(duì)對(duì)象的同一插槽,則不進(jìn)行連接,connect()返回false。
可以通過(guò)向connect()傳遞額外的參數(shù)來(lái)指定連接類(lèi)型。請(qǐng)注意,如果事件循環(huán)在接收方的線(xiàn)程中運(yùn)行,則在發(fā)送方和接收方位于不同線(xiàn)程中時(shí)使用直接連接是不安全的,原因與調(diào)用位于另一個(gè)線(xiàn)程中的對(duì)象上的任何函數(shù)是不安全。
QObject::connect()本身是線(xiàn)程安全的。Mandelbrot示例
使用排隊(duì)連接在工作線(xiàn)程和主線(xiàn)程之間進(jìn)行通信。為了避免凍結(jié)主線(xiàn)程的事件循環(huán)(以及應(yīng)用程序的用戶(hù)界面),所有Mandelbrot分形計(jì)算都在一個(gè)單獨(dú)的工作線(xiàn)程中完成。渲染完分形后,線(xiàn)程會(huì)發(fā)出一個(gè)信號(hào)。
類(lèi)似地,Blocking Fortune客戶(hù)端示例
使用單獨(dú)的線(xiàn)程與TCP服務(wù)器異步通信。
Mandelbrot示例和Blocking Fortune客戶(hù)端示例 查看這篇博客。
Thread-Support in Qt Modules Qt模塊中的線(xiàn)程支持
線(xiàn)程和SQL模塊
連接只能在創(chuàng)建它的線(xiàn)程內(nèi)使用。不支持在線(xiàn)程之間移動(dòng)連接或從其他線(xiàn)程創(chuàng)建查詢(xún)。
此外,QSqlDrivers使用的第三方庫(kù)可能會(huì)對(duì)在多線(xiàn)程程序中使用SQL模塊施加進(jìn)一步的限制。
在線(xiàn)程繪畫(huà)
QPainter可以在線(xiàn)程中用于在QImage、QPrinter和QPicture繪畫(huà)設(shè)備上繪畫(huà)。不支持在QPixmap和QWidget上繪制。在macOS上,如果從GUI線(xiàn)程外部打印,則不會(huì)顯示自動(dòng)進(jìn)度對(duì)話(huà)框。
任何數(shù)量的線(xiàn)程都可以在任何給定的時(shí)間進(jìn)行繪制,但是一次只能在給定的繪制設(shè)備上繪制一個(gè)線(xiàn)程。換言之,如果兩條線(xiàn)程分別繪制在不同的QImage上,則兩條線(xiàn)程可以同時(shí)繪制,但這兩條線(xiàn)程不能同時(shí)繪制在同一QImage上。
線(xiàn)程與富文本處理
QTextDocument、QTextCursor和所有相關(guān)的類(lèi)都是可重入的。
請(qǐng)注意,在GUI線(xiàn)程中創(chuàng)建的QTextDocument實(shí)例可能包含QPixmap圖像資源。使用QTextDocument::clone()創(chuàng)建文檔的副本,并將副本傳遞給另一個(gè)線(xiàn)程進(jìn)行進(jìn)一步處理(如打?。?。
線(xiàn)程和SVG模塊
QtSvg模塊中的QSvgGenerator和QSvgRenderer類(lèi)是可重入的。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-767584.html
線(xiàn)程和隱含共享類(lèi)
Qt對(duì)其許多值類(lèi)使用了一種稱(chēng)為隱式共享的優(yōu)化,尤其是QImage和QString。從Qt4開(kāi)始,隱式共享類(lèi)可以像任何其他值類(lèi)一樣安全地跨線(xiàn)程復(fù)制。它們是完全可重入的。隱性共享實(shí)際上是隱性的。
在許多人看來(lái),由于引用計(jì)數(shù)的典型方式,隱式共享和多線(xiàn)程是不兼容的概念。然而,Qt使用原子引用計(jì)數(shù)來(lái)確保共享數(shù)據(jù)的完整性,避免引用計(jì)數(shù)器的潛在損壞。
請(qǐng)注意,原子引用計(jì)數(shù)不能保證線(xiàn)程安全。在線(xiàn)程之間共享隱式共享類(lèi)的實(shí)例時(shí),應(yīng)使用適當(dāng)?shù)逆i定。這是對(duì)所有可重入類(lèi)的相同要求,無(wú)論是否共享。然而,原子引用計(jì)數(shù)確實(shí)保證了在隱式共享類(lèi)的本地實(shí)例上工作的線(xiàn)程是安全的。我們建議使用信號(hào)和插槽在線(xiàn)程之間傳遞數(shù)據(jù),因?yàn)檫@可以在不需要任何顯式鎖定的情況下完成。
綜上所述,Qt4中的隱式共享類(lèi)實(shí)際上是隱式共享的。即使在多線(xiàn)程應(yīng)用程序中,也可以安全地使用它們,就好像它們是普通的、非共享的、可重入的基于值的類(lèi)一樣。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-767584.html
到了這里,關(guān)于【Threads and QObjects,Thread-Support in Qt Modules】線(xiàn)程和QObjects,Qt模塊中的線(xiàn)程支持的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!