在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函數裏面定義的變量、實例等是屬於新線程的。