QT 關於使用QThread實現多線程(重寫run函數)

在QT中,有兩種多線程的方法,一種是繼承QThread的run函數,另一種是把一個繼承於QObject的類轉移到一個Thread裏。 

Qt4.8之前都是使用繼承QThread的run這種方法(本次主要介紹的就是這種),其主要步驟如下:

0x01、新建一個類(WorkThread),把該類的父類改爲QThread

0x02、重寫QThread的run函數,在run函數中搞事情(耗時操作)

0x03、在需要使用的地方,new一個子線程,然後調用QThread的 start 方法啓動線程。

0x04、如果需要結束線程,則使用QThread的 wait 方法。

 

示例代碼如下(workthread爲子線程,maindialog爲調用者):

workthread.h

#ifndef WORKTHREAD_H
#define WORKTHREAD_H

#include <QObject>
#include <QThread>

class WorkThread : public QThread/*QObject*/
{
    Q_OBJECT
public:
    explicit WorkThread(QObject *parent = 0);

signals:
    void currentNum(int);
    void workDone();

public slots:
    void stopThread();

protected:
    virtual void run();

private:
    volatile bool bStop = false;//易失性變量,用volatile進行申明
};

#endif // WORKTHREAD_H

workthread.cpp

#include "workthread.h"
#include <QDebug>
#include <QMessageBox>

WorkThread::WorkThread(QObject *parent) : QThread(parent)
{

}

void WorkThread::run()
{
    //注意:在子線程中不能出現跟UI相關的代碼(QMessageBox這些都不行)
    //ASSERT failure in QWidget: "Widgets must be created in the GUI thread.",
    //QMessageBox::information(NULL, "info", "I'm sub thread");

    //do something...
    for(int i=0; i<10; i++)
    {
        if(bStop == false)
        {
            QThread::msleep(500);
            qDebug() << "SubThreadId:" << QThread::currentThreadId() << "i:" << i;
            emit currentNum(i);
        }
    }
    emit workDone();
}

void WorkThread::stopThread()
{
    bStop = true;
}

maindialog.h

#ifndef MAINDIALOG_H
#define MAINDIALOG_H

#include <QDialog>

class MainDialog : public QDialog
{
    Q_OBJECT

public:
    MainDialog(QWidget *parent = 0);
    ~MainDialog();

public slots:
    void onCurrentNum(int);
    void onWorkDone();
};

#endif // MAINDIALOG_H

maindialog.cpp

#include "maindialog.h"
#include "workthread.h"
#include <QPushButton>
#include <QDebug>

MainDialog::MainDialog(QWidget *parent)
    : QDialog(parent)
{
    //創建"開始"按鈕 按照控件的大小創建窗口
    QPushButton * btnStart = new QPushButton("Start", this);
    //移動btnStart按鈕
    btnStart->move(100,100);
    //調整按鈕大小
    btnStart->resize(100,50);


    //創建"停止"按鈕 按照控件的大小創建窗口
    QPushButton * btnStop = new QPushButton("Stop", this);
    //移動btnStop按鈕
    btnStop->move(300,100);
    //調整按鈕大小
    btnStop->resize(100,50);


    //重置窗口大小
    resize(600,400);
    //設置固定窗口大小
    setFixedSize(600,400);
    //設置窗口標題
    setWindowTitle("QThread Test");


    WorkThread *pWork = new WorkThread(this);

    //Qt4版本的寫法
//    connect(pWork,SIGNAL(currentNum(int)),this, SLOT(onCurrentNum(int)));
//    connect(pWork,SIGNAL(workDone()),this, SLOT(onWorkDone()));


    //連接帶參數的信號和槽(函數指針的寫法)
//    void(WorkThread:: *currentNumSigal)(int) = &WorkThread::currentNum;
//    void(MainDialog:: *currentNumSlot)(int)  = &MainDialog::onCurrentNum;
//    connect(pWork, currentNumSigal, this, currentNumSlot);

//    void(WorkThread:: *workDoneSigal)(void) = &WorkThread::workDone;
//    void(MainDialog:: *workDoneSlot)(void)  = &MainDialog::onWorkDone;
//    connect(pWork, workDoneSigal, this, workDoneSlot);


    //Qt5版本寫法(Lambda表達式)
    connect(pWork, &WorkThread::currentNum, [=](int val){
        this->onCurrentNum(val);
    });

    connect(pWork, &WorkThread::workDone, [=](){
        this->onWorkDone();
    });

    //關聯Start和Stop按鈕的信號和槽
    connect(btnStart, &QPushButton::clicked, [=](){
        qDebug() << "MainThreadId:" << QThread::currentThreadId();
        pWork->start();//開啓線程
    });

    connect(btnStop, &QPushButton::clicked, [=](){
        pWork->stopThread();
        pWork->wait();//結束線程
    });
}

MainDialog::~MainDialog()
{

}

void MainDialog::onCurrentNum(int num)
{
    qDebug() << "MainDialog::onCurrentNum:" << num;
}

void MainDialog::onWorkDone()
{
    qDebug() << "MainDialog::onWorkDone()";
}

點擊Start按鈕啓動線程後,然後通過stop按鈕停止線程,在Qt Creator中應用程序輸出:

MainThreadId: 0x2638
SubThreadId: 0x3480 i: 0
MainDialog::onCurrentNum: 0
SubThreadId: 0x3480 i: 1
MainDialog::onCurrentNum: 1
SubThreadId: 0x3480 i: 2
MainDialog::onCurrentNum: 2
SubThreadId: 0x3480 i: 3
MainDialog::onCurrentNum: 3
MainDialog::onWorkDone()

可以看到:

0x01、主線程的ThreadId跟子線程的ThreadId不是同一個,證明子線程確實是開了另外一個線程。

0x02、在子線程未執行完畢前,點擊關閉程序,會提示:QThread: Destroyed while thread is still running

0x03、但是在子線程未執行完畢之前,點擊了stop按鈕後,程序停止了計數輸出,這時關閉程序,程序不會有上述提示。

 

需注意的地方:

QThread實例(WorkThread)是屬於創建該實例的線程的。比如在主線程中創建一個QThread,那麼這個QThread實例本身屬於主線程。只有子線程run函數裏面定義的變量、實例等是屬於新線程的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章