[轉]QT多線程異步調用


QT多線程異步調用,類似MFC的PostThreadMessage

blog.csdn.net/dongfangyu/article/details/5930129

 

在MFC中,每個界面線程都會有一個消息隊列,通過函數PostThreadMessage,線程之間可以互發消息,由於Post的方式是非阻塞的,因此系統可以表現出很好的性能。這種消息機制是多線程之間異步調用的極佳方式。

筆者最近學習QT4.5.3的時候,想在QT中找到類似於相似的機制,但是暫時沒有發現。於是想,沒有就創造它。以下描述我如何使QT擁有MFC的消息機制。

MFC的消息機制,其實就是通過每個消息ID對應函數地址來實現的。明白了這個,構造此機制就簡單了。首先使用StlPort的hash_map建立消息ID與回調函數地址的映射表,之所以用hash_map,是由於其時間復雜度很低,而且不會隨着表項的增多而變慢,但會增加內存使用量,在現在內存較大的年代,這個問題不大。

其次,是通過QThread繼續,得到一個基類線程,該線程假設命名MsgThd,在該線程中,配備有hash_map,同時再使用StlPort的vector建立一個消息隊列。重載QThread的run函數,其中是一個循環,該循環中不停從vector中嘗試得到消息,若得到消息ID,則嘗試根據此消息ID,從hash_map得到回調函數地址,若回調函數地址有效,則執行該函數。否則,休眠一定時間(比如1ms),然后繼續從vector中嘗試得到消息,周而復始。此時,凡是從MsgThd繼續的線程已經具有消息隊列了。

再次,如何使用該MsgThd呢?方法是,凡是需要消息隊列的線程,需從MsgThd繼續,定義消息ID,定義回調函數,然后把它們放入hash_map中。舉例,若線程A,B都是從MsgThd繼續,若線程A要給線程B發一個異步消息,那么只需要線程A往線程B的vector中放入一個消息即可,若你喜愛的話,這個動作的函數接口可以寫成PostThreadMessage。當然,存放消息的vector,須用加鎖解鎖,因為它極有可能出現線程A與線程B同時訪問的情況,在QT中,可以使用互斥量QMutex。

以上所說,是實現QT多線程異步調用的第一種方式。

 

以下所說,第二種方式。這是筆者學習QT幾天后發現的J,因為隨着對QT的熟知,接觸到越來越多的內容之后,便發現QT本身有這樣的機制。

而這種機制存在於函數QApplication::postEvent中,此函數相當於MFC的PostThreadMessage。網上有大量關於QT的訊息,但我還沒有找到對這個問題講得較清楚的。我以下解釋,不是最清楚的,但說完了,大家就會使用了。

新建一個類,比如MsgThd,繼續自QThread,重寫run函數,里面就一個函數exec();據說,exec()是消息機制的觸發函數。重寫event函數,據說,所有事件(Event)都會經過該函數,類似於MFC的PreTranslateMessage。(兩個“據說”,說明說法僅供參考,筆者也是剛接觸幾天,莫怪筆者誤人子弟。等我把QT源代碼看完之后,就沒有“據說”了,呵呵。)

 

假設線程A需要給線程B發消息,在線程A中有代碼舉例如下:

QEvent* pEvent = new QEvent((QEvent::Type)1234);

QApplication::postEvent( pThread_B, pEvent );

其中1234,是自己定義的,只要大於QT的保留值1024即可。就像你在MFC自定義消息的時候,需要大於WM_USER(0x0400)一樣。

上面兩句的意思是說,線程A產生了一個事件,它發給了線程B。

 

因此線程B中的event函數可能要這樣寫:

bool Thread_B::event(QEvent * pEvent )

{

// 截獲住自定義的事件

if ( pEvent->type() == (QEvent::Type)1234 )

{

            std::cout<<"這句話替換成你需要調用的函數"<<std::endl;

          }

return MsgThd::event(pEvent);

}

這就是第二種方式,實現QT多線程異常調用。

 

 

可以自訂事件類型,最簡單的方式,是透過QEvent::Type指定事件類型的常數值,在建構QCustomEvent時作為建構引數並透過postEvent()傳送事件,例如:

const QEvent::Type MyEvent = (QEvent::Type) 9393;
...
QApplication::postEvent(object, new QCustomEvent(MyEvent));

自訂事件必須定義事件號碼(Event number),自定義的事件號碼必須大於QEvent::Type的列舉值,通常1024以下的值是保留給Qt預先定義的事件類型來使用。

object是事件的接受者,使用 postEvent()方法時,事件必須以new的方式建立,在事件處理完畢之後,會自動將事件delete,postEvent()會將事件放至事件佇列的尾端,然後立即返回。若要強迫Qt馬上處理先前postEvent()排到佇列的事件,則可以使用sendPostedEvents()。
您可以使用sendEvent()方法,
事件會立即送至接受者,sendEvent()方法的事件不會被delete,所以通常建立在堆疊(Stack)區,例如:

CustomEvent event("Event Message");
QApplication::sendEvent(object, &event);


自訂的事件類型必須是QEvent的子類別,通常繼承QCustomEvent類別,建立自訂事件類別可以獲得更多的型態安全(Type safety)。
要處理自訂事件,可以重新定義customEvent()方法,例如:

void CustomWidget::customEvent(QCustomEvent *event) {
CustomEvent *customEvent = static_cast<CustomEvent *>(event);
    ....
}

或是重新定義event()方法,將自訂事件分派給其它函式或直接在event()中處理,例如:

bool CustomWidget::event(QEvent *event) {
    if (event->type() == MyCustomEventType) {
        CustomEvent *myEvent = static_cast<CustomEvent *>(event);
        // 對自訂事件的處理,或呼叫其它函式來處理事件
        return true;
    }
    return QWidget::event(event);
}


免責聲明!

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



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