QThread的用法

QThread的用法

概述

​ QThread類提供了一個與平臺無關的管理線程的方法。一個QThread對象管理一個線程。QThread的執行從run()函數的執行開始,在Qt自帶的QThread類中,run()函數經過調用exec()函數來啓動事件循環機制,而且在線程內部處理Qt的事件。在Qt中創建線程的主要目的就是爲了用線程來處理那些耗時的後臺操做,從而讓主界面能及時響應用戶的請求操做。QThread的使用方法有以下兩種:ide

  1. QObject::moveToThread()
  2. 繼承QThread類

下面經過具體的方法描述和例子來介紹兩種方法。函數

方法一. QObject::moveToThread()方法

方法描述

  1. 定義一個繼承於QObject的worker類,在worker類中定義一個槽slot函數doWork(),這個函數中定義線程須要作的工做。
  2. 在要使用線程的controller類中,新建一個QThread的對象和woker類對象,使用moveToThread()方法將worker對象的事件循環所有交由QThread對象處理。
  3. 創建相關的信號函數和槽函數進行鏈接,而後發出信號觸發QThread的槽函數,使其執行工做。

moveToThread的例子

​ 首先新建一個work類,該類重點在於其doWork槽函數,這個函數定義了線程須要作的工做,須要向其發送信號來觸發。Wrok類的頭文件中定義了所有函數,其cpp文件爲空,所以就不貼出來了。ui

Wroker.h的定義以下this

// work定義了線程要執行的工做
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include<QDebug>
#include<QThread>

class Worker:public QObject
{
    Q_OBJECT
public:
    Worker(QObject* parent = nullptr){}
public slots:
     // doWork定義了線程要執行的操做
    void doWork(int parameter)
    {
        qDebug()<<"receive the execute signal---------------------------------";
        qDebug()<<"     current thread ID:"<<QThread::currentThreadId();
       // 循環一百萬次
       for(int i = 0;i!=1000000;++i)
       {
        ++parameter;
       }
       // 發送結束信號
       qDebug()<<"      finish the work and sent the resultReady signal\n";
       emit resultReady(parameter);
    }

// 線程完成工做時發送的信號
signals:
    void resultReady(const int result);
};

#endif // WORKER_H

1234567891011121314151617181920212223242526272829303132333435

​ 而後定義一個Controller類,這個類中定義了一個QThread對象,用於處理worker對象的事件循環工做。spa

Controller.h的定義以下:線程

#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
#include<QThread>
#include<QDebug>

// controller用於啓動線程和處理線程執行結果
class Controller : public QObject
{
    Q_OBJECT
    QThread workerThread;
public:
    Controller(QObject *parent= nullptr);
    ~Controller();

public slots:
    // 處理線程執行的結果
    void handleResults(const int rslt)
    {
        qDebug()<<"receive the resultReady signal---------------------------------";
        qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';
        qDebug()<<"     the last result is:"<<rslt;
    }
signals:
    // 發送信號觸發線程
    void operate(const int);

};

#endif // CONTROLLER_H

12345678910111213141516171819202122232425262728293031

​ Controller類的cpp文件,其構造函數中建立worker對象,而且將其事件循環所有交給workerThread對象來處理,最後啓動該線程,而後觸發其事件處理函數。code

controller.cpp的定義以下:對象

#include "controller.h"
#include <worker.h>
Controller::Controller(QObject *parent) : QObject(parent)
{
    Worker *worker = new Worker;
    //調用moveToThread將該任務交給workThread
    worker->moveToThread(&workerThread);
    //operate信號發射後啓動線程工做
    connect(this, SIGNAL(operate(const int)), worker, SLOT(doWork(int)));
    //該線程結束時銷燬
    connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
    //線程結束後發送信號,對結果進行處理
    connect(worker, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));
    //啓動線程
    workerThread.start();
    //發射信號,開始執行
    qDebug()<<"emit the signal to execute!---------------------------------";
    qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';
    emit operate(0);
}
//析構函數中調用quit()函數結束線程
Controller::~Controller()
{
    workerThread.quit();
    workerThread.wait();
}

123456789101112131415161718192021222324252627

​ 接下來就是主函數,主函數中咱們新建一個Controller對象,開始執行:blog

main.cpp的內容以下繼承

#include <QCoreApplication>
#include "controller.h"
#include<QDebug>
#include<QThread>
int main(int argc, char *argv[])
{
    qDebug()<<"I am main Thread, my ID:"<<QThread::currentThreadId()<<"\n";
    QCoreApplication a(argc, argv);

    Controller c;
    return a.exec();
}
123456789101112

運行結果及說明

這裏寫圖片描述

運行結果截圖 1

​ main函數中打印當前線程編號,即主線程的線程編號是0X7a4, 在Controller的構造函數中繼續打印當前線程編號,也是主線程編號,以後把work類的工做交給子線程後,給子線程發送信號,子線程收到了信號開始執行,其線程號爲0X1218,執行結束後發送信號給Controller處理結果。

方法二. 繼承QThread的方法

方法描述

  1. 自定義一個繼承QThread的類MyThread,重載MyThread中的run()函數,在run()函數中寫入須要執行的工做.
  2. 調用start()函數來啓動線程。

繼承QThread的例子

​ 首先寫MyThread類,該類繼承於QThread,該類中自定義了信號槽和重寫了run函數。頭文件以下:

MyThread.h內容以下

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include<QThread>
#include<QDebug>
class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread(QObject* parent = nullptr);
    //自定義發送的信號
signals:
    void myThreadSignal(const int);
    //自定義槽
public slots:
    void myThreadSlot(const int);
protected:
    void run() override;
};

#endif // MYTHREAD_H

123456789101112131415161718192021

MyThread.cpp內容以下

#include "mythread.h"

MyThread::MyThread(QObject *parent)
{

}

void MyThread::run()
{
    qDebug()<<"myThread run() start to execute";
    qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';
    //循環一百萬次
    int count = 0;
    for(int i = 0;i!=1000000;++i)
    {
     ++count;
    }
    // 發送結束信號
    emit myThreadSignal(count);
    exec();
}

void MyThread::myThreadSlot(const int val)
{
    qDebug()<<"myThreadSlot() start to execute";
    qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';
    // 循環一百萬次
    int count = 888;
    for(int i = 0;i!=1000000;++i)
    {
     ++count;
    }
}

12345678910111213141516171819202122232425262728293031323334

​ 在Controller類中實現這MyThread的調用。

Controller.h內容以下

#include "mythread.h"

MyThread::MyThread(QObject *parent)
{

}

void MyThread::run()
{
    qDebug()<<"myThread run() start to execute";
    qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';
    // 循環一百萬次
    int count = 0;
    for(int i = 0;i!=1000000;++i)
    {
     ++count;
    }
    // 發送結束信號
    emit myThreadSignal(count);
    exec();
}

void MyThread::myThreadSlot(const int val)
{
    qDebug()<<"myThreadSlot() start to execute";
    qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';
    // 循環一百萬次
    int count = 888;
    for(int i = 0;i!=1000000;++i)
    {
     ++count;
    }
}

12345678910111213141516171819202122232425262728293031323334

Controller.cpp內容以下

#include "controller.h"
#include <mythread.h>
Controller::Controller(QObject *parent) : QObject(parent)
{
    myThrd = new MyThread;
    connect(myThrd,&MyThread::myThreadSignal,this,&Controller::handleResults);
    // 該線程結束時銷燬
    connect(myThrd, &QThread::finished, this, &QObject::deleteLater);
    connect(this,&Controller::operate,myThrd,&MyThread::myThreadSlot);
    // 啓動該線程
    myThrd->start();
    QThread::sleep(5);
    emit operate(999);
}

Controller::~Controller()
{
    myThrd->quit();
    myThrd->wait();
}
1234567891011121314151617181920

main函數的內容和上例中相同,所以就不貼了。

運行結果和說明:這裏寫圖片描述運行結果截圖2

​ 經過自定義一個繼承QThread的類,實例化該類的對象,重載run()函數爲須要作的工做。而後在須要的地方調用start函數來執行run函數中的任務。然而有趣的是,myThread.start()以後我又從主函數觸發了一個信號,對應於子線程的槽,子線程的槽函數中打印當前執行的線程的編號,能夠看到,執行子線程的槽函數的線程編號倒是主線程的編號

兩種方法的比較

​ 兩種方法來執行線程均可以,隨便你的喜歡。不過看起來第二種更加簡單,容易讓人理解。不過咱們的興趣在於這兩種使用方法到底有什麼區別?其最大的區別在於:

  1. moveToThread方法,是把咱們須要的工做所有封裝在一個類中,將每一個任務定義爲一個的槽函數,再創建觸發這些槽的信號,而後把信號和槽鏈接起來,最後將這個類調用moveToThread方法交給一個QThread對象,再調用QThread的start()函數使其全權處理事件循環。因而,任什麼時候候咱們須要讓線程執行某個任務,只須要發出對應的信號就能夠。其優勢是咱們能夠在一個worker類中定義不少個須要作的工做,而後發出觸發的信號線程就能夠執行。相比於子類化的QThread只能執行run()函數中的任務,moveToThread的方法中一個線程能夠作不少不一樣的工做(只要發出任務的對應的信號便可)。
  2. 子類化QThread的方法,就是重寫了QThread中的run()函數,在run()函數中定義了須要的工做。這樣的結果是,咱們自定義的子線程調用start()函數後,便開始執行run()函數。若是在自定義的線程類中定義相關槽函數,那麼這些槽函數不會由子類化的QThread自身事件循環所執行,而是由該子線程的擁有者所在線程(通常都是主線程)來執行。若是你不明白的話,請看,第二個例子中,子類化的線程的槽函數中輸出當前線程的ID,而這個ID竟然是主線程的ID!!事實的確是如此,子類化的QThread只能執行run()函數中的任務直到run()函數退出,而它的槽函數根本不會被本身的線程執行。

PS:

以上代碼是Qt5.7開發環境,採用的是VS2015的64位編譯器。代碼能夠直接複製粘貼運行
相關文章
相關標籤/搜索