[轉(zhuǎn)載]Qt 信號-槽 事件機制(匯總)
(2012-03-14 10:45:55)
恩,,我由回來了,使用QT了,,在windows上我發(fā)現(xiàn)使用labview做復(fù)雜協(xié)議有些難,,我又不想重新學習GTK+,所以還是使用我很鄙視的QT了,。
由于心理上喜歡C語言以及其實現(xiàn)機制,,所以對于QT的核心有警惕,挖掘挖掘其內(nèi)部知識,。
下面的資料是網(wǎng)上摘取的,,匯總一下。
--------------------------------------分割------------------------------------------------------
Qt事件和信號的區(qū)別
問題:
什么時候用事件,,什么時候用信號呢,?是不是不同的線程間用事件,信號不能用在線程間,?
解答:
仔細來看,,事件與信號其實并無多大差別,從我們對其需求上來說,,都只要能注冊事件或信號響應(yīng)函數(shù),,在事件或信號產(chǎn)生時能夠被通知到即可,。但有一項區(qū)別在
于,事件處理函數(shù)的返回值是有意義的,,我們要根據(jù)這個返回值來確定是否還要繼續(xù)事件的處理,,比如在QT中,事件處理函數(shù)如果返回true,,則這個事件處理
已完成,,QApplication會接著處理下一個事件,而如果返回false,,那么事件分派函數(shù)會繼續(xù)向上尋找下一個可以處理該事件的注冊方法,。信號處
理函數(shù)的返回值對信號分派器來說是無意義的。
另外還有一個需要我們關(guān)注的問題是事件和信號處理時的優(yōu)先級問題,。在QT中,,事件因為都是與窗口相關(guān)的,所以事件回調(diào)時都是從當前窗口開始,,一級一級向上
派發(fā),,直到有一個窗口返回true,截斷了事件的處理為止,。對于信號的處理則比較簡單,,默認是沒有順序的,如果需要明確的順序,,可以在信號注冊時顯示地指
明槽的位置,。
在QT中,事件使用了一個事件隊列來維護,,如果事件的處理中又產(chǎn)生了新的事件,,那么新的事件會加入到隊列尾,直到當前事件處理完畢后,,
QApplication再去隊列頭取下一個事件來處理,。而信號的處理方式有些不同,信號處理是立即回調(diào)的,,也就是一個信號產(chǎn)生后,,他上面所注冊的所有槽
都會立即被回調(diào)。這樣就會產(chǎn)生一個遞歸調(diào)用的問題,,比如某個信號處理器中又產(chǎn)生了一個信號,,會使得信號的處理像一棵樹一樣的展開。
評價:實際上信號-槽也可以異步的,,這個bool connect ( const QObject * sender,
const char * signal,
const QObject * receiver,
const char * method,
Qt::ConnectionType type
= Qt::AutoCompatConnection
)
里面的Qt::AutoCompatConnection,,可以根據(jù)實際情況確定
(轉(zhuǎn))signal/slot機制原理
因為最近要用到Gtk+的關(guān)系,特意扒出來惡補一下,以前用MFC和WTL比較多些,習慣了使用回調(diào)函數(shù)虛繼承和模板特化來實現(xiàn)與客戶代碼交互,猛一改過來還真有點蒙,再加上Gtk+網(wǎng)上的資料又比較少,不過好在Qt的接口設(shè)計也是通過signal/slot機制來實現(xiàn)客戶代碼交互的,趕緊找些資料參考一下,這里特別留個備份權(quán)當加強記憶吧...
信號和槽是用來在對象間通訊的方法,當一個特定事件發(fā)生的時候,,signal會被 emit 出來,,slot 調(diào)用是用來響應(yīng)相應(yīng)的
signal 的,。
QT 對象已經(jīng)包含了許多預(yù)定義的 signal,但我們總是可以在派生類中添加新的 signal,。
QT 對象中也已經(jīng)包含了許多預(yù)定義的 slog,,但我們可以在派生類中添加新的 slot 來處理我們感興趣的
signal
signal 和 slot 機制是類型安全的,signal 和
slot必須互相匹配(實際上,,一個solt的參數(shù)可以比對應(yīng)的signal的參數(shù)少,因為它可以忽略多余的參數(shù)),。signal 和
slot是松散的配對關(guān)系,,發(fā)出signal的對象不關(guān)心是那個對象鏈接了這個signal,也不關(guān)心是那個或者有多少slot鏈接到了這個
signal,。QT的signal 和
slot機制保證了,,如果一個signal和slot相鏈接,slot會在正確的時機被調(diào)用,,并且是使用正確的參數(shù),。Signal和slot都可以攜帶任
何數(shù)量和類型的參數(shù),他們都是類型安全的,。
所有從QObject直接或者間接繼承出來的類都能包含信號和槽,,當一個對象的狀態(tài)發(fā)生變化的時候,信號就可以被emit出來,,這可能是某個其它的對象所
關(guān)心的,。這個對象并不關(guān)心有那個對象或者多少個對象鏈接到這個信號了,這是真實的信息封裝,,它保證了這個對象可以作為一個軟件組件來被使用,。
槽(slot)是用來接收信號的,但同時他們也是一個普通的類成員函數(shù),,就象一個對象不關(guān)心有多少個槽鏈接到了它的某個信號,,一個對象也不關(guān)心一個槽鏈接了多少個信號。這保證了用QT創(chuàng)建的對象是一個真實的獨立的軟件組件,。
一個信號可以鏈接到多個槽,,一個槽也可以鏈接多個信號。同時,,一個信號也可以鏈接到另外一個信號,。
所有使用了信號和槽的類都必須包含 Q_OBJECT
宏,而且這個類必須從QObject類派生(直接或者間接派生)出來,,
當一個signal被emit出來的時候,,鏈接到這個signal的slot會立刻被調(diào)用,就好像是一個函數(shù)調(diào)用一樣,。當這件事情發(fā)生的時
候,,signal和slot機制與GUI的事件循環(huán)完全沒有關(guān)系,,當所有鏈接到這個signal的slot執(zhí)行完成之后,在 emit
代碼行之后的代碼會立刻被執(zhí)行,。當有多個slot鏈接到一個signal的時候,,這些slot會一個接著一個的、以隨機的順序被執(zhí)行,。
Signal 代碼會由 moc
自動生成,,開發(fā)人員一定不能在自己的C++代碼中實現(xiàn)它,并且,,它永遠都不能有返回值,。
Slot其實就是一個普通的類函數(shù),并且可以被直接調(diào)用,,唯一特殊的地方是它可以與signal相鏈接,。
C++的預(yù)處理器更改或者刪除 signal, slot, emit
關(guān)鍵字,所以,,對于C++編譯器來說,,它處理的是標準的C++源文件。
signal/slot
在底層會使用三種方式傳遞消息,。bool QObject::connect ( const QObject * sender,
const char * signal, const QObject * receiver, const char * method,
Qt::ConnectionType type = Qt::AutoCompatConnection )
最后一個參數(shù)是就是傳遞消息的方式了,,有四個取值:
Qt::DirectConnection
When emitted, the signal is
immediately delivered to the slot.
假設(shè)當前有4個slot連接到QPushButton::clicked(bool),當按鈕被按下時,,QT就把這4個slot按連接的時間順序調(diào)用一遍,。顯然這種方式不能跨線程(傳遞消息)。
Qt::QueuedConnection
When emitted, the signal is queued
until the event loop is able to deliver it to the
slot.
假
設(shè)當前有4個slot連接到QPushButton::clicked(bool),,當按鈕被按下時,,QT就把這個signal包裝成一個
QEvent,放到消息隊列里,。QApplication::exec()或者線程的QThread::exec()會從消息隊列里取消息,,然后調(diào)用
signal關(guān)聯(lián)的幾個slot。這種方式既可以在線程內(nèi)傳遞消息,,也可以跨線程傳遞消息,。
Qt::BlockingQueuedConnection
Same as QueuedConnection, except that
the current thread blocks until the slot has been delivered. This
connection type should only be used for receivers in a different
thread. Note that misuse of this type can lead to dead locks in
your application.
與Qt::QueuedConnection類似,但是會阻塞等到關(guān)聯(lián)的slot都被執(zhí)行,。這里出現(xiàn)了阻塞這個詞,,說明它是專門用來多線程間傳遞消息的。
Qt::AutoConnection
If the signal is emitted from the
thread in which the receiving object lives, the slot is invoked
directly, as with Qt::DirectConnection; otherwise the signal is
queued, as with Qt::QueuedConnection.
這種連接類型根據(jù)signal和slot是否在同一個線程里自動選擇Qt::DirectConnection或Qt::QueuedConnection
這樣看來,,第一種類型的效率肯定比第二種高,,畢竟第二種方式需要將消息存儲到隊列,而且可能會涉及到大對象的復(fù)制(考慮sig_produced(BigObject
bo),bo需要復(fù)制到隊列里),。
Qt的線程和 signal-slot
再問coredump或者QT高手們一個問題,。QT里用線程,主線程是GUI,,次線程是工作線程,,如果次線程里也用消息循環(huán)(exec),即,,主/次間用signal-slot通訊來實現(xiàn)同步,,在這種情況下,
1.主線程會被次線程block嗎,? 2.如果在單cpu里會被block嗎,,如果次線程里不斷循環(huán),低層會自動分配時間片給主線程嗎,?
3.這種方法和直接調(diào)用次線程中的方法,用mutex之類的來實現(xiàn)類似的循環(huán),,效率差別大嗎,?記得QT里講過signal-slot只是多幾個函數(shù)調(diào)用的差別。
呵呵,,問的不太清楚,,主要就是想問一下,主次線程中怎樣通訊比較有效,。 謝謝阿,。
QThread 現(xiàn)在默認狀態(tài)下就是有一個消息循環(huán)的,而且鼓勵使用線程獨立的消息循環(huán) 1.主線程會被次線程block嗎,?
不會,,QObject::connectd的最后一個參數(shù)( http://doc.qt./4.6/qt.html#ConnectionType-enum)
默認使用Qt::AutoConnection,
在多線程環(huán)境下,會自動選擇Qt::QueuedConnection方式,,除非強制使用Qt::BlockingQueuedConnection 或者
Qt::DirectConnection,, 否則線程不會block.
2.如果在單cpu里會被block嗎,如果次線程里不斷循環(huán),,低層會自動分配時間片給主線程嗎,?
線程和CPU個數(shù)沒有關(guān)系,當然CPU多了,,運行會更加流程,,線程的資源分配(時間片)是由操作系統(tǒng)管理的,在各個系統(tǒng)上都是如此
3.這種方法和直接調(diào)用次線程中的方法,,用mutex之類的來實現(xiàn)類似的循環(huán),,效率差別大嗎?
執(zhí)行效率上很難講,但是mutex肯定是帶來了開發(fā)效率的降低,你得小心翼翼地處理好各種臨界資源的鎖問題 4.
記得QT里講過signal-slot只是多幾個函數(shù)調(diào)用的差別
是的,,signal-slot的這種輕微的效率損失在絕大多數(shù)情況下不是問題,,除非過度使用,這一般是設(shè)計上的錯誤導(dǎo)致的
分享兩篇文章,,講QThread和Qt的signal-slot的: 1篇是qt labs的博客上一篇 You are doing it
wrong...(http://labs./blogs/2010/06/17/youre-doing-it-wrong/),
這篇文章很有意思,,其實很多人不知道原來QThread還可以這樣用。
另外一篇是關(guān)于怎么利用Qt的signal-slot機制和QThread進行完全的lock free編程(http:///blog/2009 ...
ti-threading-in-qt/)
多謝多謝,,再學習去,。
之所以問,是因為,,我做了一個下載googlemap的程序,,開始下載線程后,GUI就不動了,,直到下載結(jié)束,。雖然操作系統(tǒng)會分配時間,但是如果下載線程循環(huán),,而且優(yōu)先級相同的話,,是不是還是有影響?
我再看看哪里出問題了,。 剛看了你上面的兩個鏈接,,真是切中要害阿,呵呵,,一直就在琢磨這個一段時間了,。 總結(jié)一下理解,看看對不對,。
第一篇里,,QThread里默認的run()已經(jīng)啟動了消息循環(huán)exec(). 當需要新線程時,應(yīng)該,,直接生成QThread,
start(),。至于功能,應(yīng)該放在另外的類里,,在調(diào)用start前設(shè)置好signal-slot,,再moveToThread到
QThread中。 不用sunbclass QThread,。 我一直或者重新實現(xiàn)run(),,在run()里生成所有object,
signal-slot, 或者如文中所說的QThread.moveToThread(QThread).
這兩種都有不少問題,破壞OO的概念,。 很清楚,。
第二篇里講了用線程間的signal-slot來同步,正好回答了之前的疑問,不過他使用qthread的方法恰恰違背了第一篇的思想,。
引用:
原帖由 GPS 于 12-7-2010 12:40 發(fā)表 我再看看哪里出問題了,。 剛看了你上面的兩個鏈接,真是切中要害阿,,呵呵,,一直就在琢磨這個一段時間了。
總結(jié)一下理解,,看看對不對,。 第一篇里,QThread里默認的run()已經(jīng)啟動了消息循環(huán)exec(). 當需要新線程時
...
另一種方式也不錯,,有些情況下必須subclass QThread, 錯的不是是否subclass,,錯在:subclassing QThread, adding signal
and slot code to that subclass, and then calling
moveToThread(this);
引用:
To set the
record straight: I’m not saying sub-classing QThread is wrong. This
is also how Java does it (sort of), and many people have been doing
this with Qt for a long time. What I’m saying is “wrong” is
subclassing QThread, adding signal and slot code to that subclass,
and then calling moveToThread(this); in the constructor of that
subclass. This causes people lots of confusion from my experience
and observation on the #qt channel and the qt-interest mailing
list. And yes, the current docs are still a bit lacking, something
that I am fully aware of (and take responsibility for). I’m
planning on sitting down and fleshing them out, showing both ways
of using QThread (with and without
subclassing).
在Qt的編程模型里,thread和signal-
slot都是實現(xiàn)異步編程的手段,,signal-slot的背后是消息循環(huán),,而每個thread都是一個獨立的消息循環(huán),在早期版本,,qt線程間無法進行
signal-slot消息傳遞,,這就導(dǎo)致了各個消息循環(huán)都是互相獨立的loop, 通訊的唯一方式只剩下global state+
mutex lock。在4.x 之后,,signal-slot已經(jīng)能夠和thread融洽相處了。
這樣,,理想的QT編程模型就變成了獨立的一個個任務(wù),,各自
使用自己的thread和thread內(nèi)部消息循環(huán),在需要互相通訊的時候,,使用signal-slot,
這些signal和slot就是明確定義的消息接口,,除此之外,最好不共享其它狀態(tài),。Qt程序員有點像轉(zhuǎn)盤子的雜技演員,,每個盤子都獨自轉(zhuǎn)動,整個系統(tǒng)由
很多旋轉(zhuǎn)的盤子組成,,這些盤子可以共享雜技演員一個手,,也可以是另一只手甚至不同的雜技演員來控制(多CPU)。
[ 本帖最后由 coredump 于 12-7-2010 12:55 編輯 ]
引用:
原帖由 coredump 于 12-7-2010 12:14 發(fā)表 不應(yīng)該,。
搞清楚了,,忘了QThread.moveToThread(this)了,結(jié)果是下載線程對象里的slots也在主線程了調(diào)用了,。多謝指點,。
|