注意:本文停止更新,請優先考慮 Qt 線程基礎(QThread、QtConcurrent等) dbzhang800 2011.06.18 QThread 似乎是很難的一個東西,特別是信號和槽,有非常多的人(盡管使用者本人往往不知道)在用不恰當(甚至錯誤)的方式在使用 QThread,隨便用google一搜,就能搜出大量結果出來。無怪乎Qt的開發人員 Bradley T. Hughes 聲嘶力竭地喊you are-doing-it-wrong 和眾多用戶一樣,初次看到這個時,感到 Bradley T. Hughes有 些莫名奇妙,小題大作。盡管不舒服,當時還是整理過一篇博客QThread 的使用方法 時間過去3個月,盡管依然沒怎么用thread;但今天csdn論壇中有人問到這個問題,想想還是盡我所能整理一下吧。提升自己,方便他人,何樂而不為呢? QThread東西還是比較多的,而且我對底層對象了解有限,僅就一點進行展開(或許是大家最關心的一點):QThread中的slots在那個線程中執行? QThread::runrun 函數是做什么用的?Manual中說的清楚:
原文如下(這段話我們稱為定理一吧):
這么短的文字一眼就看完了,可是,這是什么意思呢?又能說明什么問題呢?看段簡單代碼: class Thread:public QThread { Q_OBJECT public: Thread(QObject* parent=0):QThread(parent){} public slots: void slot() { ... } signals: void sig(); protected: void run() { ...} }; int main(int argc, char** argv) { ... Thread thread; ... } 對照前面的定理,run函數中的代碼時確定無疑要在次線程中運行的,那么其他的呢?比如 slot 是在次線程還是主線程中運行? 你想說主線程,但又心有不甘,對么? QObject::connect涉及信號槽,我們就躲不過 connect 函數,只是這個函數大家太熟悉。我不好意思再用一堆廢話來描述它,但不說又不行,那么折中一下,只看它的最后一個參數吧(為了簡單起見,只看它最常用的3個值) 下面的列表,我們暫稱為定理二:
同前面一樣,這些文字大家都能看懂。但含義呢? 不妨繼續拿前面的例子來看,slot 函數是在主線程還是次線程中執行呢? 定理二強調兩個概念:發送信號的線程 和 接收者所依附的線程。而 slot 函數屬於我們在main中創建的對象 thread,即thread依附於主線程
太繞了?不是么(要徹底理解這幾句話,你可能需要看Qt meta-object系統和Qt event系統) 怎么辦呢?如果上兩節看不懂,就記住下面的話吧(自己總結的,用詞上估計會不太准確)。
好了,不再添加更多文字了,看代碼,估計咱們都會輕松點 主線程(信號)QThread(槽)這是 Qt Manual 和 例子中普遍采用的方法。 但由於manual沒說槽函數是在主線程執行的,所以不少人都認為它應該是在次線程執行了。
/*! * \file main.cpp * * Copyright (C) 2010, dbzhang800 * All rights reserved. * */ #include <QtCore/QCoreApplication> #include <QtCore/QObject> #include <QtCore/QThread> #include <QtCore/QDebug> class Dummy:public QObject { Q_OBJECT public: Dummy(){} public slots: void emitsig() { emit sig(); } signals: void sig(); }; class Thread:public QThread { Q_OBJECT public: Thread(QObject* parent=0):QThread(parent) { //moveToThread(this); } public slots: void slot_main() { qDebug()<<"from thread slot_main:" <<currentThreadId(); } protected: void run() { qDebug()<<"thread thread:"<<currentThreadId(); exec(); } }; #include "main.moc" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug()<<"main thread:"<<QThread::currentThreadId(); Thread thread; Dummy dummy; QObject::connect(&dummy, SIGNAL(sig()), &thread, SLOT(slot_main())); thread.start(); dummy.emitsig(); return a.exec(); } 然后看到結果(具體值每次都變,但結論不變) main thread: 0x1a40 from thread slot_main: 0x1a40 thread thread: 0x1a48 看到了吧,槽函數的線程和主線程是一樣的! 如果你看過Qt自帶的例子,你會發現 QThread 中 slot 和 run 函數共同操作的對象,都會用QMutex鎖住。為什么? 因為slot和run處於不同線程,需要線程間的同步! 如果想讓槽函數slot在次線程運行(比如它執行耗時的操作,會讓主線程死掉),怎么解決呢?
main thread: 0x13c0 thread thread: 0x1de0 from thread slot_main: 0x1de0 這可以工作,但這是 Bradley T. Hughes 強烈批判的用法。推薦的方法后面會給出。 run中信號與QThread中槽
/*! * \file main.cpp * * Copyright (C) 2010, dbzhang800 * All rights reserved. * */ #include <QtCore/QCoreApplication> #include <QtCore/QObject> #include <QtCore/QThread> #include <QtCore/QDebug> class Dummy:public QObject { Q_OBJECT public: Dummy(QObject* parent=0):QObject(parent){} public slots: void emitsig() { emit sig(); } signals: void sig(); }; class Thread:public QThread { Q_OBJECT public: Thread(QObject* parent=0):QThread(parent) { //moveToThread(this); } public slots: void slot_thread() { qDebug()<<"from thread slot_thread:" <<currentThreadId(); } signals: void sig(); protected: void run() { qDebug()<<"thread thread:"<<currentThreadId(); Dummy dummy; connect(&dummy, SIGNAL(sig()), this, SLOT(slot_thread())); dummy.emitsig(); exec(); } }; #include "main.moc" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug()<<"main thread:"<<QThread::currentThreadId(); Thread thread; thread.start(); return a.exec(); } 想看結果么? main thread: 0x15c0 thread thread: 0x1750 from thread slot_thread: 0x15c0
如何解決呢?
推薦的方法千呼萬喚始出來。 其實,這個方法太簡單,太好用了。定義一個普通的QObject派生類,然后將其對象move到QThread中。使用信號和槽時根本不用考慮多線程的存在。也不用使用QMutex來進行同步,Qt的事件循環會自己自動處理好這個。 /*! * \file main.cpp * * Copyright (C) 2010, dbzhang800 * All rights reserved. * */ #include <QtCore/QCoreApplication> #include <QtCore/QObject> #include <QtCore/QThread> #include <QtCore/QDebug> class Dummy:public QObject { Q_OBJECT public: Dummy(QObject* parent=0):QObject(parent) {} public slots: void emitsig() { emit sig(); } signals: void sig(); }; class Object:public QObject { Q_OBJECT public: Object(){} public slots: void slot() { qDebug()<<"from thread slot:" <<QThread::currentThreadId(); } }; #include "main.moc" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug()<<"main thread:"<<QThread::currentThreadId(); QThread thread; Object obj; Dummy dummy; obj.moveToThread(&thread); QObject::connect(&dummy, SIGNAL(sig()), &obj, SLOT(slot())); thread.start(); dummy.emitsig(); return a.exec(); } 結果:恩,slot確實不在主線程中運行(這么簡單不值得歡呼么?) main thread: 0x1a5c from thread slot: 0x186c 其他
|
轉自:http://blog.csdn.net/an505479313/article/details/50351745#t8