QT多執行緒訊號槽機制關鍵點

2021-10-07 11:41:28 字數 3849 閱讀 8636

qt 訊號槽機制

多執行緒下qt注意點

一些qt心得知識點

這個問題,重在搞明白qt的訊號、槽函式在何時、何地、由誰發出、由誰執行。

不要小看這個例子,筆者見過一些「用qt工作過五六年」的人士,被問到該問題時還是「王顧左右而言他」,不知道該怎麼回答。可以想象,這些人只能算處於使用 qt的初級階段,連核心問題的門都還沒有摸到。

在回答這個問題前,我們必須要介紹一些基礎知識。

給出乙個**片段,藉此說明問題:

class mythread:public qthread

//p1物件在舊執行緒

void run()//p2物件在新執行緒

}void mian()

qt 多執行緒下只有qthread::run() 函式是在新執行緒中。

run中new的物件,是在新執行緒中。

除此以外,建構函式中new的物件,執行緒物件本身,還是在舊執行緒中。

由於mythread的建構函式還是在主線程中呼叫的,所以p1是在主線程中。

這幾點非常關鍵。

第五個引數代表槽函式在哪個執行緒中執行 :

1)自動連線(autoconnection),預設的連線方式,如果訊號與槽,也就是傳送者與接受者在同一執行緒,等同於直接連線;如果傳送者與接受者處在不同執行緒,等同於佇列連線。

4)鎖定佇列連線(queuedconnection)

5)單一連線(queuedconnection)

qt::uniqueconnection:這個flag可以通過按位或(|)與以上四個結合在一起使用。當這個flag設定時,當某個訊號和槽已經連線時,再進行重複的連線就會失敗。也就是避免了重複連線

注意:此處教科書說法「可能」有歧義錯誤。後續我們會糾正這裡的錯誤。

如果你第一次看到這樣的說法,有沒有對「傳送者」這個概念感到疑惑。或者說,僅僅用這裡的描述,你可以回答最開篇我們的提問嗎?

在開篇的例子中,我們加入乙個新的限定,訊號和槽採用直連線方式。

1)pa 屬於主線程

2)採用直接連線方式

訊號傳送者是誰,是主線程還是子執行緒,還是其它什麼「東西」 ?

注:不清楚這個概念,無法清晰回答左邊三個問題,就還不算完全理解了多執行緒下訊號槽機制。

答案:訊號的傳送者是執行緒,不是其它「東西」,而且是子執行緒。由於是直連方式,槽函式是在子執行緒中呼叫。

何時呼叫?類似函式指標的方式,在emit提交的時候,直接類似呼叫「函式指標」的方式立刻在子執行緒中執行。

第五個引數代表槽函式在哪個執行緒中執行 :

1)自動連線(autoconnection),預設的連線方式,如果訊號與槽,也就是「傳送訊號的執行緒」與「接受者所在的執行緒」是同一執行緒,等同於直接連線;如果「傳送訊號的執行緒」與「接受者所在的執行緒」不是乙個執行緒,等同於佇列連線。

2)直接連線(directconnection),當訊號發射時,槽函式立即直接呼叫。無論槽函式所屬物件在哪個執行緒,槽函式總在「傳送訊號的執行緒」中執行,即槽函式和「訊號傳送執行緒」在同一執行緒

4)鎖定佇列連線(queuedconnection)

qt::blockingqueuedconnection:槽函式的呼叫時機與qt::queuedconnection一致,不過傳送完訊號後「傳送訊號的執行緒5)單一連線(queuedconnection)

qt::uniqueconnection:這個flag可以通過按位或(|)與以上四個結合在一起使用。當這個flag設定時,當某個訊號和槽已經連線時,再進行重複的連線就會失敗。也就是避免了重複連線。

「傳送訊號的執行緒」就是指**中呼叫 emit 訊號時的執行執行緒;而不是「訊號所在物件「所屬的執行緒。

三個要素,來決定槽函式在哪個執行緒呼叫。

呼叫emit 傳送訊號的執行緒。

接受者物件所在的執行緒。

connect的第五個引數。

注:槽函式在何處執行是動態決定的,而不是在寫connect函式時(編譯時)決定的。

每個執行緒均有自己的訊息事件迴圈佇列

直連方式,是所謂立即執行,就是函式直接呼叫。

佇列方式,不會立即執行,分單/多執行緒情況:

單執行緒(傳送、接收同一執行緒):訊息放入訊息佇列。執行緒進入訊息佇列時,依次執行佇列上訊息對應槽函式。

跨執行緒(傳送、接收不同執行緒):訊息放入接收者執行緒佇列。接收者執行緒執行時(可能阻塞、睡眠、或退出),進入它的訊息佇列後,依次執行佇列上的訊息。這也意味著,同乙個槽函式不會重入,不用考慮重入互斥訪問,因為都在佇列上排隊等待依次執行(注意不要亂用postevents函式,後續我們有機會單獨講一講該問題)。

class thread :public qthread;

virtual ~thread();

protected:

void run()

private:

test * m_outobj;

};int main(int argc, char *ar**)

如果你完全理解了上述要素,這個例子會有什麼問題嗎?

分析如下:

t 呼叫movetothread後,t作為接受者,已經被移到到子執行緒中去。

子執行緒執行start後,run中呼叫emit訊號觸發後,子執行緒不會立刻執行槽函式;而是等到子執行緒回到事件迴圈後,才會執行槽函式。但是子執行緒此時會直接退出;不會再有機會回到子執行緒的事件迴圈。

最終導致槽函式沒有機會再去執行。

2 非主線程不能操作ui控制項,否則qt崩潰。這也是要分離介面與邏輯的重要原因。[重要]

3 父子qobject物件,必須在同乙個執行緒;不同執行緒的物件,不能是父子關係。

否則會報錯,或者產生未知情況。[重要]

4 官方優先推薦使用movetothread方式,其次使用繼承qthread方式。理解後都一樣。

6 要注意volidate修飾共享變數、要注意加鎖。不同的鎖行為會導致執行緒不同狀態,得根據執行緒業務狀態去考慮用什麼鎖。[重要]

7 活用慎用processevents

執行緒(包括主線程和其它執行緒)執行很繁重的計算任務中,為防止訊息事件一直無機會處理,則在函式中手動呼叫processevents,讓訊息事件有機會更新。介面不會假死。

另一方面,槽函式中不可呼叫processevents,因為這會導致「中斷」當前槽函式,進而去執行訊息佇列中後續的槽函式,可能會引發同一槽函式重入問題,將程式設計問題複雜化。

8 new qobject物件哪些需要手動釋放?

乙個qobject物件,如果有父節點,則可以不手動delete釋放,父節點釋放時會自動去釋放所有子節點;反之沒有父節點,須手動呼叫delete釋放。

delete qobject時,會把物件從父節點摘掉;再刪除並釋放它的所有子節點。一些addchild操作,會主動把物件加入父節點。父子關係不是可有可無的,會涉及到物件記憶體**問題,要做到心中有數。

尾語

最近面試過一些號稱做過多年qt開發的程式設計師,有些連qt第5個引數要麼沒聽過,要麼聽過卻沒有深入理解原理。可想而知,這些人在平時工作中要麼沒有深入思考,要麼沒有深入刨根問底。或許更多的人處於沒有機會去觸及這些本質問題。因為他們實在太忙了,忙於低水平的原地重複。

寫此文,純粹是為了「治病救人」。限於本人水平有限,如有錯誤,還請賜教討論。

QT多執行緒 訊號與槽機制

qt應用程式只能在主線程重新整理控制項。如果想在其他執行緒重新整理控制項,需要傳送訊號給主線程,由主線程負責呼叫相應的函式來重新整理。pyqt5中,使用訊號需要匯入pyqtsignal。from pyqt5.qtcore import pyqtsignal,qobject建立自定義訊號 建立自定義訊...

qt 多執行緒 訊號槽

qt訊號多執行緒 當你connect的時候,型別為qt autoconnection就好了 如果slot在同一執行緒就是直接呼叫,如果slot在另外乙個執行緒就通過事件機制處理 在qt中,你的執行緒在呼叫exec函式後就進入了事件迴圈機制 這時,如果另外乙個執行緒傳送了乙個訊號過來 1 如果接收執行...

Qt訊號槽機制

c 的rtti機制功能有限 qt提供了元物件系統,使用該系統的基類qobject所建立和派生的類,可以再執行期獲取該物件的類名 父 類名,成員變數資訊,成員函式資訊,進而實現訊號槽機制。c 模擬 類定義檔案 object.h 1.ifndef db object 2.define db object...