QThread的常見特性:html
run()是線程的入口,就像main()對於應用程序的做用。QThread中對run()的默認實現調用了exec(),從而建立一個QEventLoop對象,由其處理該線程事件隊列(每個線程都有一個屬於本身的事件隊列)中的事件。簡單用代碼描述以下:安全
int QThread::exec() { //... QEventLoop eventLoop; int returnCode = eventLoop.exec(); //... return returnCode; } int QEventLoop::exec(ProcessEventsFlags flags) { //... while (!d->exit) { while (!posted_event_queue_is_empty) { process_next_posted_event(); } } //... }
因而可知,exec()在其內部不斷作着循環遍歷事件隊列的工做,調用QThread的quit()或exit()方法使中止工做,儘可能不要使用terminate(),該方法過於粗暴,形成資源不能釋放,甚至互斥鎖還處於加鎖狀態。函數
1. 舊的使用方式:oop
#include "QThread" #include "QMutexLocker" #include "QMutex" class Thread:public QThread { Q_OBJECT public: Thread(); void stop(); private: bool m_stopFlag; QMutex mutex; protected: void run(); }; Thread::Thread() { m_stopFlag = false; } void Thread::stop() { QMutexLocker locker(&mutex); m_stopFlag = true; } void Thread::run() { while(1){ { QMutexLocker locker(&mutex); if(m_stopFlag) break; } qDebug()<<"This is in thread["<<currentThreadId()<<"]."<<(int)currentThread(); sleep(2); } m_stopFlag = false; }
這是qt4.6及以前的使用方法,這種方式本沒有什麼錯誤,能夠處理咱們的絕大多數需求。下面的調用能夠看出Thread對象自己工做在主線程下,即便調用的t.stop()方法,它也是工做在主線程下,只有run()範圍內的代碼工做在次線程中。post
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug()<<"From main thread: "<<QThread::currentThreadId(); Thread t; QObject::connect(&t, SIGNAL(finished()), &a, SLOT(quit())); t.start(); return a.exec(); }
2. 推薦的使用方式:ui
#include <QtCore> class Worker : public QObject { Q_OBJECT private slots: void onTimeout() { qDebug()<<"Worker::onTimeout get called from?: "<<QThread::currentThreadId(); } }; #include "main.moc" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug()<<"From main thread: "<<QThread::currentThreadId(); QThread t; QTimer timer; Worker worker; QObject::connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout())); timer.start(1000); worker.moveToThread(&t); t.start(); return a.exec(); }
這是Qt4.7及之後版本推薦的工做方式。其主要特色就是利用Qt的事件驅動特性,將須要在次線程中處理的業務放在獨立的模塊(類)中,由主線程建立完該對象後,將其移交給指定的線程,且能夠將多個相似的對象移交給同一個線程。在這個例子中,信號由主線程的QTimer對象發出,以後Qt會將關聯的事件放到worker所屬線程的事件隊列。因爲隊列鏈接的做用,在不一樣線程間鏈接信號和槽是很安全的。spa
說說connect最後一個參數,鏈接類型:線程
1)自動鏈接(AutoConnection),默認的鏈接方式,若是信號與槽,也就是發送者與接受者在同一線程,等同於直接鏈接;若是發送者與接受者處在不一樣線程,等同於隊列鏈接。code
2)直接鏈接(DirectConnection),當信號發射時,槽函數當即直接調用。不管槽函數所屬對象在哪一個線程,槽函數總在發送者所在線程執行。htm
3)隊列鏈接(QueuedConnection),當控制權回到接受者所在線程的事件循環時,槽函數被調用。槽函數在接受者所在線程執行。
3.GUI界面假死的處理
在GUI程序中,主線程也叫GUI線程,由於它是惟一被容許執行GUI相關操做的線程。對於一些耗時的操做,若是放在主線程中,就是出現界面沒法響應的問題。這種問題的解決一種方式是,把這些耗時操做放到次線程中,還有一種比較簡單的方法:在處理耗時操做中頻繁調用QApplication::processEvents()。這個函數告訴Qt去處理那些尚未被處理的各種事件,而後再把控制權返還給調用者。
QElapsedTimer et; et.start(); while(et.elapsed()<300) QCoreApplication::processEvents();
http://blog.debao.me/2013/08/how-to-use-qthread-in-the-right-way-part-1/
http://qt-project.org/doc/qt-4.8/qthread.html
http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/