run()是線程的入口,就像main()對於應用程序的做用。QThread中對run()的默認實現調用了exec(),從而建立一個QEventLoop對象,由其處理該線程事件隊列(每個線程都有一個屬於本身的事件隊列)中的事件。簡單用代碼描述以下:安全
1 int QThread::exec() 2 { 3 //... 4 QEventLoop eventLoop; 5 int returnCode = eventLoop.exec(); 6 //... 7 return returnCode; 8 } 9 10 int QEventLoop::exec(ProcessEventsFlags flags) 11 { 12 //... 13 while (!d->exit) { 14 while (!posted_event_queue_is_empty) { 15 process_next_posted_event(); 16 } 17 } 18 //... 19 }
因而可知,exec()在其內部不斷作着循環遍歷事件隊列的工做,調用QThread的quit()或exit()方法使中止工做,儘可能不要使用terminate(),該方法過於粗暴,形成資源不能釋放,甚至互斥鎖還處於加鎖狀態。函數
這是qt4.6及以前的使用方法,這種方式本沒有什麼錯誤,能夠處理咱們的絕大多數需求。Thread對象自己工做在主線程下,即便調用的t.stop()方法,它也是工做在主線程下,只有run()範圍內的代碼工做在次線程中。oop
1 class WorkerThread : public QThread 2 { 3 Q_OBJECT 4 5 void run() Q_DECL_OVERRIDE { 6 QString result; 7 emit resultReady(result); 8 } 9 10 signals: 11 void resultReady(const QString &s); 12 }; 13 14 void MyObject::startWorkInAThread() 15 { 16 WorkerThread *workerThread = new WorkerThread(this); 17 connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults); 18 connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater); 19 workerThread->start(); 20 }
從Qt4.8起,能夠釋放運行剛剛結束的線程對象,經過鏈接finished()信號到QObject::deleteLater()。post
2.推薦的使用方式:ui
這是Qt4.7及之後版本推薦的工做方式。其主要特色就是利用Qt的事件驅動特性,將須要在次線程中處理的業務放在獨立的模塊(類)中,由主線程建立完該對象後,將其移交給指定的線程,且能夠將多個相似的對象移交給同一個線程。在這個例子中,信號由主線程的QTimer對象發出,以後Qt會將關聯的事件放到worker所屬線程的事件隊列。因爲隊列鏈接的做用,在不一樣線程間鏈接信號和槽是很安全的。
說說connect最後一個參數,鏈接類型:
1)自動鏈接(AutoConnection),默認的鏈接方式,若是信號與槽,也就是發送者與接受者在同一線程,等同於直接鏈接;若是發送者與接受者處在不一樣線程,等同於隊列鏈接。
2)直接鏈接(DirectConnection),當信號發射時,槽函數當即直接調用。不管槽函數所屬對象在哪一個線程,槽函數總在發送者所在線程執行。this
3)隊列鏈接(QueuedConnection),當控制權回到接受者所在線程的事件循環時,槽函數被調用。槽函數在接受者所在線程執行。spa
1 class Worker : public QObject 2 { 3 Q_OBJECT 4 5 public slots: 6 void doWork(const QString meter) { 7 // ... 8 emit resultReady(result); 9 } 10 void stopWork(){ 11 //... 12 } 13 signals: 14 void resultReady(const QString &result); 15 }; 16 17 class Controller : public QObject 18 { 19 Q_OBJECT 20 21 QThread workerThread; 22 23 public: 24 Controller() { 25 Worker *worker = new Worker; 26 worker->moveToThread(&workerThread); 27 connect(workerThread, &QThread::finished, worker, &QObject::deleteLater); 28 connect(this, &Controller::operate, worker, &Worker::doWork); 29 connect(this, &Controller::kill, worker, &Worker::stopWork); 30 connect(worker, &Worker::resultReady, this, &Controller::handleResults); 31 workerThread.start(); 32 } 33 ~Controller() { 34 workerThread.quit(); 35 workerThread.wait(); 36 } 37 38 public slots: 39 void handleResults(const QString &); 40 41 signals: 42 void operate(const QString &); 43 void kill(); 44 };
使用新方式,子線程中的槽都在子線程中運行,主線程中的槽都在主線程中運行,信號和槽默認使用自動鏈接(AutoConnection)。值得注意的是,若是槽doWork中有耗時操做,好比說while循環,主線程的信號kill子線程是不會響應的,除非使用直接鏈接(DirectConnection),connect(this, &Controller::kill, worker, &Worker::stopWork, Qt::DirectConnection);,此時,槽stopWork工做於主線程。
.net
3.GUI界面假死的處理
在GUI程序中,主線程也叫GUI線程,由於它是惟一被容許執行GUI相關操做的線程。對於一些耗時的操做,若是放在主線程中,就是出現界面沒法響應的問題。這種問題的解決一種方式是,把這些耗時操做放到次線程中,還有一種比較簡單的方法:在處理耗時操做中頻繁調用QApplication::processEvents()。這個函數告訴Qt去處理那些尚未被處理的各種事件,而後再把控制權返還給調用者。線程