Qt信號槽的一些事

注:此文是站在Qt5的角度說的,對於Qt4部分是不適用的。git

1.先說Qt信號槽的幾種鏈接方式和執行方式。

1)Qt信號槽給出了五種鏈接方式:github

Qt::AutoConnection 0 自動鏈接:默認的方式。信號發出的線程和糟的對象在一個線程的時候至關於:DirectConnection, 若是是在不一樣線程,則至關於QueuedConnection
Qt::DirectConnection 1 直接鏈接:至關於直接調用槽函數,可是當信號發出的線程和槽的對象再也不一個線程的時候,則槽函數是在發出的信號中執行的。
Qt::QueuedConnection 2 隊列鏈接:內部經過postEvent實現的。不是實時調用的,槽函數永遠在槽函數對象所在的線程中執行。若是信號參數是引用類型,則會另外複製一份的。線程安全的。
Qt::BlockingQueuedConnection 3 阻塞鏈接:此鏈接方式只能用於信號發出的線程(通常是先好對象的線程) 和 槽函數的對象再也不一個線程中才能用。經過信號量+postEvent實現的。不是實時調用的,槽函數永遠在槽函數對象所在的線程中執行。可是發出信號後, 當前線程會阻塞,等待槽函數執行完畢後才繼續執行。
Qt::UniqueConnection 0x80 防止重複鏈接。若是當前信號和槽已經鏈接過了,就再也不鏈接了。

2)信號槽的調用方式和線程:安全

UniqueConnection 模式:嚴格說不算鏈接方式,方式就是4中,此只是一個附加的參數。不討論。異步

AutoConnection 模式:這個模式是默認的,但其能夠看做是DirectConnection和QueuedConnection的自動選擇,直接分析那兩種也就好了。函數

發出信號,調用槽的方式也能夠簡單的分爲兩種:同步調用和異步調用post

同步調用:發出信號後,當前線程等待槽函數執行完畢後才繼續執行。性能

異步調用:發出信號後,當即執行剩下邏輯,不關心槽函數何時執行。測試

因此有下表:spa

線程/模式 DirectConnection QueuedConnection BlockingQueuedConnection
相同線程 直接調用,同步調用。 經過事件進行隊列調用。異步調用. 不可用
不一樣線程 直接調用。同步調用。

槽函數在發出信號的線程執行。線程

有線程安全隱患。

 

經過事件進行隊列調用。異步調用.

槽函數在對象所在的線程執行。

線程安全。

經過事件進行阻塞調用。同步調用。

槽函數在對象所在的線程執行。

線程安全。

2.Qt信號鏈接多個槽,調用順序。

先說基本原則:

槽函數開始調用的順序和鏈接的順序是一致的。

可是,上面也說了,有同步調用和異步調用。

對於同步調用,你觀察的結果和基本原則同樣。

可是對於異步調用,可能你最早鏈接的它,可是可能其餘都執行完畢了,可是其還沒執行。是由於對於異步調用:是開始調用的時候,生成一個須要調用這個 函數的事件,而後放到事件隊列裏。而後當即返回,去執行調用其餘槽函數或者槽函數都執行了,不關心槽函數的執行狀態的。等到事件隊列裏任務輪到此事件再去 調用。

3.信號的返回值。

大都說Qt信號槽不能使用返回值。其實不不許確的,Qt5中,信號槽是有返回值的。只是Qt的一個信號能夠鏈接多個槽,還有同步調用和異步調用的問題,沒發支持的很好,因此,返回值雖有,但只是雞肋。

先說下返回值的規則把:

  • 同步調用纔有返回值,異步調用的返回值永遠爲返回值類型默認構造函數出來的。

  • 鏈接的多個槽都返回值,那麼結果是最後調用(鏈接)的那個。

也就是說對於QueuedConnection鏈接的信號槽,永遠只是返回返回類型的默認構造函數的。對於AutoConnection鏈接的,若是發出信號的線程和槽函數線程不一樣亦然。

測試小例子地址:https://github.com/dushibaiyu/DsbyLiteExample/tree/master/QtSignalsSlotTest

4.信號參數的安全問題:

由於一個信號能夠鏈接多個槽函數,若是參數是T * 或者是T &話會不會第一個槽函數改變參數的值,而後第二此調用的參數就已經不是信號發出的值?

1)對於T &: 在同步調用中則是變化的,不可用於異步,不可跨線程。因此BlockingQueuedConnection方式的同步也不行。(T& 不可用在隊列調用(QueuedConnection)和阻塞調用(BlockingQueuedConnection)中。只能使用const T &。)

由於同步調用,你能夠理解成直接調用,那麼鏈接多個槽函數就至關於直接連續調用多個函數。相似於:

01 // 函數原型都是:void  (int &a )
02 int a;
03 fun1(a);
04 fun2(a)
05 ·····
06
07 // 函數原型都是:void  (int * a )
08 int a;
09 pfun1(&a);
10 pfun2(&a)
11 ·····

這樣,當第一個函數執行改變參數值以後,其後的函數調用都要受影響。

2) 對於T *,最好不要同時鏈接多個槽。

對於同步調用:是一個接着一個調用的,執行順序相似上面,因此值也是每次調用也會變化的。

對於異步調用:其內容確實不肯定的,由於異步調用的時間是不可控的。若是還有跨線程相關,則還有線程安全問題。

5.信號槽性能損失:

注:僅僅代碼層進行的理論分析,非實際測試,不嚴謹,不權威。

關於信號槽(不少吐槽Qt就是說的這個):

(1)Qt4語法的,都說是匹配字符串,其實只是連接信號槽的用的匹配字符串 的方法,經過字符串找到信號和槽在QMeatObject裏存的索引位置int類型,還有槽函數的索引,而後調用的時候經過索引號用switch去區分的 發射的那個函數,而後取出對應的連接槽的list,循環檢測槽函數的參數是否匹配,而後調用槽函數。。這個連接時會耗時查找,可是你能有多少信號?這個鏈 接也耗時很少,調用的時候耗時主要就是在參數匹配上了。

(2)Qt5 語法的,Qt5 的槽函數連接和執行是基於模板實現的,函數對象。信號和槽的參數問題是編譯時檢查的,執行效率更高,可是編譯就慢點了。連接時也是經過信號的地址找到其的 信號索引,至於槽函數直接是生成一個函數對象的,而後調用的時候也是先switch找到發射的信號,取出list,而後逐個調用其儲存的函數對象,因此對 於Qt5 語法的信號槽,調用性能損失幾乎能夠說無的。

(3)連接的信號槽的時候,Qt::UniqueConnection的連接方式會對已經連接過的此先好的槽函數進行遍歷,會有連接時的損失。其餘連接的損失就在上面說過了。
(3)在信號槽調用的時候,還有一些連接方式和線程的判斷和爲了安全問題的鎖操做。關於這個就還涉及到調用槽函數的線程問題。

對於同線程直接調用,較函數對象直接調用的損失,就只有連接方式和線程的判斷的幾個if 分支和 鎖的操做。
對於線程間通信的調用,跨線程。信號槽內部也是經過Qt事件循環機制實現的,跨線程就不是時時調用了,主要是安全了,對於性能有沒有損失無法評論的。對於跨線程阻塞的調用,這個也是事件實現,只是但發射信號的線程會阻塞,這個找不到對應的直接調用的比較,也很差說。
關於信號槽Qt是做何不少方便使用和安全調用,較之函數指針,性能會有損失,可是也沒損失多少的。對於函數對象調用,Qt5語法的調用,幾乎是不損失什麼的。

 

注:此文是我的根據文檔,源碼和本身寫小例子測試得出的總結,若有錯誤請您指出。

相關文章
相關標籤/搜索