1. 程式人生 > >[Qt學習篇]Qthread實現多執行緒操作

[Qt學習篇]Qthread實現多執行緒操作

一、QThread類概述                                       

QThread類為使用者管理多執行緒提供了一種平臺無關的途徑。

#include <QThread>

繼承自QObject類

二、QThread類詳述                                       

QThread物件在程式內部進行控制執行緒的管理,QThread起始於run()函式額執行。預設情況下,run()通過呼叫exec()啟動事件迴圈(event loop),並在執行緒內部執行Qt 的事件迴圈。

以下示例通過QObject::moveToThread()

呼叫把worker物件新增到執行緒中執行:

class Worker : public QObject
 {
     Q_OBJECT
     QThread workerThread;
 
 public slots:
     void doWork(const QString str) {
         // ...
         emit resultReady(result);
     }
 
 signals:
     void resultReady(const QString &result);
 };
 
 class Controller : public QObject
 {
     Q_OBJECT
     QThread workerThread;
 public:
     Controller() {
         Worker *worker = new Worker;
         worker->moveToThread(&workerThread);
         connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
         connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString)));
         connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
         workerThread.start();
     }
     ~Controller() {
         workerThread.quit();
         workerThread.wait();
     }
 public slots:
     void handleResults(const QString &);
 signals:
     void operate(const QString &);
 };

Worker槽中的程式碼以後就在一個獨立的執行緒中執行。你可以按自己意願,把Worker槽(slots)連線到任何物件的訊號(signal)上。得益於佇列連線(queued connections) 機制(一個事件被派發到接收者所線上程的事件迴圈,當事件被傳遞,相應的槽隨之被啟用),在不同物件間進行訊號和槽的連線是安全的。

另外一種實現多執行緒的方式是繼承QThread類,並重新實現run()函式,例如:

class Thread : public QThread
{   
    Q_OBJECT
public:
    Thread();
    void setMessage(const QString &message);
    void stop();
protected:
    void run();
private:
    QString messageStr;
    volatile bool stopped;
    QUdpSocket *udpSpcket;
};
class ThreadDialog : public QDialog
{
    Q_OBJECT
public:
    ThreadDialog(QWidget *parent = 0);
protected:
    void closeEvent(QCloseEvent *event);
private slots:
    void startOrStopThreadA();
    void startOrStopThreadB();
private:
    Thread threadA;
    Thread threadB;
    QPushButton *threadAButton;
    QPushButton *threadBButton;
};

注意

1、當run()函式返回的以後,執行緒會退出(exit)。如果沒有呼叫exec()函式,該執行緒中不存在任何執行狀態的事件迴圈(event loop)。

2、重要的是,QThread物件通常存在於(lives in)建立他的執行緒中,而不是它管理的執行緒中。這意味著,QThread的槽是在它存在的執行緒(home thread)的上下文中被執行,而不是它管理的執行緒的上下文中。基於這一點,在QThread子類中實現新的槽是容易出錯所以是不被提倡的。

3、如果使用其他方法(technique)在不同物件間進行互動,而不是佇列性的訊號-槽(signal/slot)機制,那麼在多執行緒程式中就要預先警惕可能出現的問題。

4、GUI物件的執行緒必須存在於主執行緒(main thread)中。

三、管理QThread                                      

Managing threads

當執行緒開始執行(started())、執行完畢(finished())、終止(terminated())的時候,QThread通過訊號()進行通知;反過來,也可以呼叫isFinished()isRunning()查詢執行緒的狀態。

要想停止(stop)執行緒,可以通過呼叫exit()quit()實現,在極端情況下,可以呼叫terminate()強制終止一個執行狀態的執行緒——但是這樣比較危險,並不提倡,使用setTerminationEnabled()函式可以使能/禁止terminate()函式。

Qt 4.8以後,通過把finished()訊號連線到QObject::deleteLater()槽上,可以線上程結束後刪除(deallocate)物件。

使用wait()可以阻塞呼叫執行緒,直到其他執行緒執行結束(也可以指定一個超時時間)。

靜態(static)函式currentThreadId()currentThread()返回當前執行執行緒的的識別符號(identifiers),

線上程啟動前呼叫setObjectName()可以給執行緒指定一個名字,作為和其他執行緒的區別標識。如果沒有呼叫setObjectName(),將以執行緒物件執行時型別的類名作為執行緒的名字。比如,下面例子中,執行緒的名字是RenderThread:

class RenderThread : public QThread
{
     Q_OBJECT
 public:
     RenderThread(QObject *parent = 0);
     ~RenderThread();
     void render(double centerX, double centerY, double scaleFactor, QSize resultSize); 

 signals:
     void renderedImage(const QImage &image, double scaleFactor);
 protected:
     void run();
 private:
     uint rgbFromWaveLength(double wave);
     QMutex mutex;
     QWaitCondition condition;  
};
注意:這種規則目前不適用於在Windows閃編譯的release。

QThread類提供了static型別,平臺無關的sleep函式:sleep()msleep()usleep(),經度分別是秒、毫秒、微秒。

注意:一般來講,wait()sleep()應該是不必要的(unnecessary),因為Qt是基於事件驅動(event-driven)的架構。使用wait()的時候,考慮下finished(),sleep()時,考慮下QTimer。

四、QThread優先順序                                  

enum Qthread::Priority

常量

描述

QThread::IdlePriority

0

沒有其他執行緒執行時才進行排程

QThread::LowestPriority

1

不比LowPriority排程頻繁

QThread::LowPriority

2

不比NormalPriority排程頻繁

QThread::NormalPriority

3

作業系統的預設優先順序

QThread::HighPriority

4

比NormalPriority排程頻繁

QThread::HighestPriority

5

HighPriority排程頻繁

QThread::TimeCriticalPriority

6

儘可能頻繁的進行排程

QThread::InheritPriority

7

使用和建立自己的執行緒同樣的優先順序,這是預設屬性