注:此文是站在Qt5的角度說的,對於Qt4部分是不適用的。git
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 |
相同線程 | 直接調用,同步調用。 | 經過事件進行隊列調用。異步調用. | 不可用 |
不一樣線程 | 直接調用。同步調用。 槽函數在發出信號的線程執行。線程 有線程安全隱患。
|
經過事件進行隊列調用。異步調用. 槽函數在對象所在的線程執行。 線程安全。 |
經過事件進行阻塞調用。同步調用。 槽函數在對象所在的線程執行。 線程安全。 |
先說基本原則:
槽函數開始調用的順序和鏈接的順序是一致的。
可是,上面也說了,有同步調用和異步調用。
對於同步調用,你觀察的結果和基本原則同樣。
可是對於異步調用,可能你最早鏈接的它,可是可能其餘都執行完畢了,可是其還沒執行。是由於對於異步調用:是開始調用的時候,生成一個須要調用這個 函數的事件,而後放到事件隊列裏。而後當即返回,去執行調用其餘槽函數或者槽函數都執行了,不關心槽函數的執行狀態的。等到事件隊列裏任務輪到此事件再去 調用。
大都說Qt信號槽不能使用返回值。其實不不許確的,Qt5中,信號槽是有返回值的。只是Qt的一個信號能夠鏈接多個槽,還有同步調用和異步調用的問題,沒發支持的很好,因此,返回值雖有,但只是雞肋。
先說下返回值的規則把:
同步調用纔有返回值,異步調用的返回值永遠爲返回值類型默認構造函數出來的。
鏈接的多個槽都返回值,那麼結果是最後調用(鏈接)的那個。
也就是說對於QueuedConnection鏈接的信號槽,永遠只是返回返回類型的默認構造函數的。對於AutoConnection鏈接的,若是發出信號的線程和槽函數線程不一樣亦然。
測試小例子地址:https://github.com/dushibaiyu/DsbyLiteExample/tree/master/QtSignalsSlotTest
由於一個信號能夠鏈接多個槽函數,若是參數是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 *,最好不要同時鏈接多個槽。
對於同步調用:是一個接着一個調用的,執行順序相似上面,因此值也是每次調用也會變化的。
對於異步調用:其內容確實不肯定的,由於異步調用的時間是不可控的。若是還有跨線程相關,則還有線程安全問題。
注:僅僅代碼層進行的理論分析,非實際測試,不嚴謹,不權威。
關於信號槽(不少吐槽Qt就是說的這個):
(1)Qt4語法的,都說是匹配字符串,其實只是連接信號槽的用的匹配字符串 的方法,經過字符串找到信號和槽在QMeatObject裏存的索引位置int類型,還有槽函數的索引,而後調用的時候經過索引號用switch去區分的 發射的那個函數,而後取出對應的連接槽的list,循環檢測槽函數的參數是否匹配,而後調用槽函數。。這個連接時會耗時查找,可是你能有多少信號?這個鏈 接也耗時很少,調用的時候耗時主要就是在參數匹配上了。
(2)Qt5 語法的,Qt5 的槽函數連接和執行是基於模板實現的,函數對象。信號和槽的參數問題是編譯時檢查的,執行效率更高,可是編譯就慢點了。連接時也是經過信號的地址找到其的 信號索引,至於槽函數直接是生成一個函數對象的,而後調用的時候也是先switch找到發射的信號,取出list,而後逐個調用其儲存的函數對象,因此對 於Qt5 語法的信號槽,調用性能損失幾乎能夠說無的。
(3)連接的信號槽的時候,Qt::UniqueConnection的連接方式會對已經連接過的此先好的槽函數進行遍歷,會有連接時的損失。其餘連接的損失就在上面說過了。
(3)在信號槽調用的時候,還有一些連接方式和線程的判斷和爲了安全問題的鎖操做。關於這個就還涉及到調用槽函數的線程問題。
對於同線程直接調用,較函數對象直接調用的損失,就只有連接方式和線程的判斷的幾個if 分支和 鎖的操做。
對於線程間通信的調用,跨線程。信號槽內部也是經過Qt事件循環機制實現的,跨線程就不是時時調用了,主要是安全了,對於性能有沒有損失無法評論的。對於跨線程阻塞的調用,這個也是事件實現,只是但發射信號的線程會阻塞,這個找不到對應的直接調用的比較,也很差說。
關於信號槽Qt是做何不少方便使用和安全調用,較之函數指針,性能會有損失,可是也沒損失多少的。對於函數對象調用,Qt5語法的調用,幾乎是不損失什麼的。
注:此文是我的根據文檔,源碼和本身寫小例子測試得出的總結,若有錯誤請您指出。