1. 程式人生 > >信號槽 與事件區別

信號槽 與事件區別

消息傳遞 only nbc 普通 阻塞 send cat 自動分配 aware

信號槽 與事件區別

仔細來看,事件與信號其實並無多大差別,從我們對其需求上來說,都只要能註冊事件或信號響應函數,在事件或信號產生時能夠被通知到即可。但有一項區別在於,事件處理函數的返回值是有意義的,我們要根據這個返回值來確定是否還要繼續事件的處理,比如在QT中,事件處理函數如果返回true,則這個事件處理 已完成,QApplication會接著處理下一個事件,而如果返回false,那麽事件分派函數會繼續向上尋找下一個可以處理該事件的註冊方法。信號處 理函數的返回值對信號分派器來說是無意義的。

另外還有一個需要我們關註的問題是事件和信號處理時的優先級問題。在QT中,事件因為都是與窗口相關的,所以事件回調時都是從當前窗口開始,一級一級向上 派發,直到有一個窗口返回true,截斷了事件的處理為止。對於信號的處理則比較簡單,默認是沒有順序的,如果需要明確的順序,可以在信號註冊時顯示地指 明槽的位置。 在QT中,事件使用了一個事件隊列來維護,如果事件的處理中又產生了新的事件,那麽新的事件會加入到隊列尾,直到當前事件處理完畢後, QApplication再去隊列頭取下一個事件來處理。而信號的處理方式有些不同,信號處理是立即回調的,也就是一個信號產生後,他上面所註冊的所有槽 都會立即被回調。這樣就會產生一個遞歸調用的問題,比如某個信號處理器中又產生了一個信號,會使得信號的處理像一棵樹一樣的展開。

評價:實際上信號-槽也可以異步的,這個bool connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoCompatConnection )
裏面的Qt::AutoCompatConnection,可以根據實際情況確定

所有使用了信號和槽的類都必須包含 Q_OBJECT 宏,而且這個類必須從QObject類派生(直接或者間接派生)出來,
當一個signal被emit出來的時候,鏈接到這個signal的slot會立刻被調用,就好像是一個函數調用一樣。當這件事情發生的時 候,signal和slot機制與GUI的事件循環完全沒有關系,當所有鏈接到這個signal的slot執行完成之後,在 emit 代碼行之後的代碼會立刻被執行。當有多個slot鏈接到一個signal的時候,這些slot會一個接著一個的、以隨機的順序被執行。
Signal 代碼會由 moc 自動生成,開發人員一定不能在自己的C++代碼中實現它,並且,它永遠都不能有返回值。
Slot其實就是一個普通的類函數,並且可以被直接調用,唯一特殊的地方是它可以與signal相鏈接。
C++的預處理器更改或者刪除 signal, slot, emit 關鍵字,所以,對於C++編譯器來說,它處理的是標準的C++源文件。

signal/slot 在底層會使用三種方式傳遞消息。bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoCompatConnection )
最後一個參數是就是傳遞消息的方式了,有四個取值:

1.Qt::DirectConnection
When emitted, the signal is immediately delivered to the slot.
假設當前有4個slot連接到QPushButton::clicked(bool),當按鈕被按下時,QT就把這4個slot按連接的時間順序調用一遍。顯然這種方式不能跨線程(傳遞消息)。

2.Qt::QueuedConnection
When emitted, the signal is queued until the event loop is able to deliver it to the slot.
假 設當前有4個slot連接到QPushButton::clicked(bool),當按鈕被按下時,QT就把這個signal包裝成一個 QEvent,放到消息隊列裏。QApplication::exec()或者線程的QThread::exec()會從消息隊列裏取消息,然後調用 signal關聯的幾個slot。這種方式既可以在線程內傳遞消息,也可以跨線程傳遞消息。 3.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類似,但是會阻塞等到關聯的slot都被執行。這裏出現了阻塞這個詞,說明它是專門用來多線程間傳遞消息的。 4.

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.
這種連接類型根據signal和slot是否在同一個線程裏自動選擇Qt::DirectConnection或Qt::QueuedConnection

這樣看來,第一種類型的效率肯定比第二種高,畢竟第二種方式需要將消息存儲到隊列,而且可能會涉及到大對象的復制(考慮sig_produced(BigObject bo),bo需要復制到隊列裏)。

Qt的線程和 signal-slot

再問coredump或者QT高手們一個問題。QT裏用線程,主線程是GUI,次線程是工作線程,如果次線程裏也用消息循環(exec),即,主/次間用signal-slot通訊來實現同步,在這種情況下, 1.主線程會被次線程block嗎? 2.如果在單cpu裏會被block嗎,如果次線程裏不斷循環,低層會自動分配時間片給主線程嗎? 3.這種方法和直接調用次線程中的方法,用mutex之類的來實現類似的循環,效率差別大嗎?記得QT裏講過signal-slot只是多幾個函數調用的差別。 呵呵,問的不太清楚,主要就是想問一下,主次線程中怎樣通訊比較有效。 謝謝阿。

QThread 現在默認狀態下就是有一個消息循環的,而且鼓勵使用線程獨立的消息循環 1.主線程會被次線程block嗎? 不會,QObject::connectd的最後一個參數默認使用Qt::AutoConnection, 在多線程環境下,會自動選擇Qt::QueuedConnection方式,除非強制使用Qt::BlockingQueuedConnection或者 Qt::DirectConnection, 否則線程不會block. 2.如果在單cpu裏會被block嗎,如果次線程裏不斷循環,低層會自動分配時間片給主線程嗎? 線程和CPU個數沒有關系,當然CPU多了,運行會更加流程,線程的資源分配(時間片)是由操作系統管理的,在各個系統上都是如此 3.這種方法和直接調用次線程中的方法,用mutex之類的來實現類似的循環,效率差別大嗎? 執行效率上很難講,但是mutex肯定是帶來了開發效率的降低,你得小心翼翼地處理好各種臨界資源的鎖問題 4. 記得QT裏講過signal-slot只是多幾個函數調用的差別 是的,signal-slot的這種輕微的效率損失在絕大多數情況下不是問題,除非過度使用,這一般是設計上的錯誤導致的 分享兩篇文章,講QThread和Qt的signal-slot的: 1篇是qt labs的博客上一篇 You are doing it wrong...(http://labs.trolltech.com/blogs/2010/06/17/youre-doing-it-wrong/), 這篇文章很有意思,其實很多人不知道原來QThread還可以這樣用。 另外一篇是關於怎麽利用Qt的signal-slot機制和QThread進行完全的lock free編程(http://thesmithfam.org/blog/2009 ... ti-threading-in-qt/)

多謝多謝,再學習去。 之所以問,是因為,我做了一個下載googlemap的程序,開始下載線程後,GUI就不動了,直到下載結束。雖然操作系統會分配時間,但是如果下載線程循環,而且優先級相同的話,是不是還是有影響?

我再看看哪裏出問題了。 剛看了你上面的兩個鏈接,真是切中要害阿,呵呵,一直就在琢磨這個一段時間了。 總結一下理解,看看對不對。 第一篇裏,QThread裏默認的run()已經啟動了消息循環exec(). 當需要新線程時,應該,直接生成QThread, start()。至於功能,應該放在另外的類裏,在調用start前設置好signal-slot,再moveToThread到 QThread中。 不用sunbclass QThread。 我一直或者重新實現run(),在run()裏生成所有object, signal-slot, 或者如文中所說的QThread.moveToThread(QThread). 這兩種都有不少問題,破壞OO的概念。 很清楚。

第二篇裏講了用線程間的signal-slot來同步,正好回答了之前的疑問,不過他使用qthread的方法恰恰違背了第一篇的思想。

引用:
原帖由 GPS 於 12-7-2010 12:40 發表 我再看看哪裏出問題了。 剛看了你上面的兩個鏈接,真是切中要害阿,呵呵,一直就在琢磨這個一段時間了。 總結一下理解,看看對不對。 第一篇裏,QThread裏默認的run()已經啟動了消息循環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都是實現異步編程的手段,signal-slot的背後是消息循環,而每個thread都是一個獨立的消息循環,在早期版本,qt線程間無法進行 signal-slot消息傳遞,這就導致了各個消息循環都是互相獨立的loop, 通訊的唯一方式只剩下global state+ mutex lock。在4.x 之後,signal-slot已經能夠和thread融洽相處了。 這樣,理想的QT編程模型就變成了獨立的一個個任務,各自 使用自己的thread和thread內部消息循環,在需要互相通訊的時候,使用signal-slot, 這些signal和slot就是明確定義的消息接口,除此之外,最好不共享其它狀態。Qt程序員有點像轉盤子的雜技演員,每個盤子都獨自轉動,整個系統由 很多旋轉的盤子組成,這些盤子可以共享雜技演員一個手,也可以是另一只手甚至不同的雜技演員來控制(多CPU)

信號槽 與事件區別