QT出現重復調用SLOT函數


QT出現重復調用SLOT函數

來源 https://blog.csdn.net/luzheqi/article/details/53046062

 

問題表現

問題是這樣的,在每次重復一些操作之后,總會給我多次調用。而且很有規律,第一輪調用1次,第二輪調用2次,第三輪調用3次,以此類推。

問題修改

源代碼是這樣的。

connect(rtmpSource, SIGNAL(showCameraInterrtpt()), this, SLOT(showCameraMessageBox());
這樣就會有問題,默認的connect每次調用都會建立一個連接,改成下面的代碼就好了。

connect(rtmpSource, SIGNAL(showCameraInterrtpt()), this, SLOT(showCameraMessageBox()), Qt::UniqueConnection);
問題原因

Qt信號槽給出了五種連接方式,分別如下。

默認的方式是Qt::AutoConnection,這樣是不行的,重復調用connect會建立多個映射,這個時候應該使用Qt::UniqueConnection,這樣就沒有問題了。

 

信號槽的調用方式和線程

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

AutoConnection 模式:這個模式是默認的,但其可以看作是DirectConnection和QueuedConnection的自動選擇,直接分析那兩種也就行了。

 

發出信號,調用槽的方式也可以簡單的分為兩種:同步調用和異步調用

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

異步調用:發出信號后,立即執行剩下邏輯,不關心槽函數什么時候執行。

所以有下表:

 

QT信號連接多個槽,調用順序

先說基本原則:

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

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

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

但是對於異步調用,可能你最先連接的它,但是可能其他都執行完畢了,但是其還沒執行。是因為對於異步調用:是開始調用的時候,生成一個需要調用這個函數的事件,然后放到事件隊列里。然后立即返回,去執行調用其他槽函數或者槽函數都執行了,不關心槽函數的執行狀態的。等到事件隊列里任務輪到此事件再去調用。

 

信號的返回值

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

先說下返回值的規則把:
- 同步調用才有返回值,異步調用的返回值永遠為返回值類型默認構造函數出來的。
- 連接的多個槽都返回值,那么結果是最后調用(連接)的那個。

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

 

信號參數的安全問題

因為一個信號可以連接多個槽函數,如果參數是T * 或者是T &的話會不會第一個槽函數改變參數的值,然后第二此調用的參數就已經不是信號發出的值?

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

因為同步調用,你可以理解成直接調用,那么連接多個槽函數就相當於直接連續調用多個函數。類似於:

// 函數原型都是:void (int &a )
int a;
fun1(a);
fun2(a)
·····

// 函數原型都是:void (int * a )
int a;
pfun1(&a);
pfun2(&a)
·····

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

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

對於同步調用:是一個接着一個調用的,執行順序類似上面,所以值也是每次調用也會變化的。
對於異步調用:其內容確實不確定的,因為異步調用的時間是不可控的。如果還有跨線程相關,則還有線程安全問題。

 

信號槽性能損失:

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

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

(1)Qt4語法的,都說是匹配字符串,其實只是鏈接信號槽的用的匹配字符串 的方法,通過字符串找到信號和槽在QMeatObject里存的索引位置int類型,還有槽函數的索引,然后調用的時候通過索引號用switch去區分的 發射的那個函數,然后取出對應的鏈接槽的list,循環檢測槽函數的參數是否匹配,然后調用槽函數。。這個鏈接時會耗時查找,但是你能有多少信號?這個鏈 接也耗時不多,調用的時候耗時主要就是在參數匹配上了。
(2)Qt5 語法的,Qt5 的槽函數鏈接和執行是基於模板實現的,函數對象。信號和槽的參數問題是編譯時檢查的,執行效率更高,但是編譯就慢點了。鏈接時也是通過信號的地址找到其的 信號索引,至於槽函數直接是生成一個函數對象的,然后調用的時候也是先switch找到發射的信號,取出list,然后逐個調用其儲存的函數對象,所以對 於Qt5 語法的信號槽,調用性能損失幾乎可以說無的。
(3)鏈接的信號槽的時候,Qt::UniqueConnection的鏈接方式會對已經鏈接過的此先好的槽函數進行遍歷,會有鏈接時的損失。其他鏈接的損失就在上面說過了。
(4)在信號槽調用的時候,還有一些鏈接方式和線程的判斷和為了安全問題的鎖操作。關於這個就還涉及到調用槽函數的線程問題。
對於同線程直接調用,較函數對象直接調用的損失,就只有鏈接方式和線程的判斷的幾個if 分支和 鎖的操作。
對於線程間通訊的調用,跨線程。信號槽內部也是通過Qt事件循環機制實現的,跨線程就不是時時調用了,主要是安全了,對於性能有沒有損失沒法評論的。對於跨線程阻塞的調用,這個也是事件實現,只是但發射信號的線程會阻塞,這個找不到對應的直接調用的比較,也不好說。
關於信號槽Qt是作何很多方便使用和安全調用,較之函數指針,性能會有損失,但是也沒損失多少的。對於函數對象調用,Qt5語法的調用,幾乎是不損失什么的。

============ End

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM