Qt中連線到同一signal的多個slots的執行順序問題

iteye_20954發表於2011-12-24
  • in the order they have been connected

起源

前些天忘記在哪兒討論過這個問題,今天在csdn又看到有網友問這個問題,而其他網友卻無一例外的給出了“無序”這個答案。

Manual

Qt的問題,當manual中有明確文字說明時,我們應該以Qt的manual為準:

http://doc.qt.nokia.com/4.8/signalsandslots.html

If several slots are connected to one signal, the slots will be executed one after the other, in the order they have been connected, when the signal is emitted.

或者http://doc.qt.nokia.com/4.8/qobject.html

If a signal is connected to several slots, the slots are activated in the same order as the order the connection was made, when the signal is emitted.

恩,說的很明確,各個槽按照被connect的順序被執行【注意,在佇列模式時,是指相應的事件被post,仍不保證槽被按照該順序執行】。

可是,為什麼很多人認為是無序呢?(除了前面提及的,還有什麼其他因素麼?)

翻翻Qt4.5以及該版本之前的manual,可以看到

http://doc.qt.nokia.com/4.5/signalsandslots.html

If several slots are connected to one signal, the slots will be executed one after the other, in an arbitrary order, when the signal is emitted.

以及http://doc.qt.nokia.com/4.5/qobject.html

If a signal is connected to several slots, the slots are activated in an arbitrary order when the signal is emitted.

網路上以及書籍中的大部分資料都是Qt4.6之前的,故爾 ...

原始碼

恩,嘗試看過元物件系統這部分原始碼的網友對這部分不會覺得陌生。如果沒看過,可以瞅瞅這個Qt Meta Object system 學習(三)

  • QObject::connect() 最終將 訊號和槽的索引值放置到一個Connection列表中

QObjectPrivate::Connection *c = new QObjectPrivate::Connection; 
    c->sender = s; 
    c->receiver = r; 
    c->method = method_index; 
    c->connectionType = type; 
    c->argumentTypes = types; 
    c->nextConnectionList = 0; 
QObjectPrivate::get(s)->addConnection(signal_index, c); 
  • 訊號的發射,就是藉助QMetaObject::activate() 來依次處理前面那個Connection列表總的項

do { 
        QObjectPrivate::Connection *c = connectionLists->at(signal_index).first; 
        if (!c) continue; 
        // We need to check against last here to ensure that signals added  
        // during the signal emission are not emitted in this emission. 
        QObjectPrivate::Connection *last = connectionLists->at(signal_index).last;
 
       do { 
            if (!c->receiver) 
                continue; 
             QObject * const receiver = c->receiver;

在該過程中:

  • 如果是直接連線,則通過 QMetaObject::metacall() 直接呼叫
  • 如果是佇列連線,則post一個QMetaCallEvent事件,稍後通過事件派發,該事件的處理函式負責通過 QMetaObject::metacall() 呼叫相應的槽函式【注意,小心此時槽函式的執行順序】

注意:此處的程式碼片段是Qt4的,而Qt5.0 中由於引入了新式的訊號和槽語法,對應部分原始碼有較大變化。

參考


相關文章