關於使用繼承QObject實現多執行緒的理解——Qt推薦的方法
概念
多執行緒的使用主要是為了處理比較耗時的過程。這可以用以下圖來形象地描述:
目前,由於繼承QObject的多執行緒實現方法更加靈活,Qt官方推薦使用該方法實現多執行緒。
想用圖來描述實現的過程,發現也不好表達,將就著看吧:
步驟
1、創鍵一個繼承於 QObject 的自定義執行緒類(如:MyThread),用來盛放比較耗時,需要放入子執行緒的處理函式
- 定義一個執行緒處理函式(如:MyWork),當然也可以定義多個,這時多個處理函式就共用一個子執行緒
- 在處理函式中進行處理,此過程可能時間較長(如:QThread::sleep(1))
- 在處理函式中傳送處理完成的訊號(如:emit signal_back()),當然該訊號中可能含有處理的結果資訊(如計算結果)
2、 在主執行緒(亦稱介面執行緒)中建立一個子執行緒(QThread* subthread = new QThread(this))
3、新建一個自定義執行緒類物件(MyThread* m_MyThread = new MyThread())
4、將自定義執行緒類物件移入子執行緒容器中(m_MyThread->moveToThread(subthread)),其實也可以移入多個自定義執行緒類到同一個subthread中,這時他們就共享一個子執行緒了
5、連線主執行緒和子執行緒之間的訊號和槽
- 主執行緒——>子執行緒,主執行緒的訊號和子執行緒的槽
connect ( this, &MainWindow::StartThread, m_MyThread, &MyThread::MyWork )
- 子執行緒——>主執行緒,子執行緒的訊號和主執行緒的槽
connect (m_MyThread, &MyThread::signal_back, this, &MainWindow::slot_handle_finish )
6、子執行緒使用完畢需要回收銷燬,不然該執行緒會一直佔用,而系統可分配的執行緒數量是有限的
subthread->qiut(); //該停止函式比較弱,會等到執行緒處理函式MyWork()的任務執行完之後,才會停止執行緒
subthread->wait();
所有通常會加入一個標誌位,來終止任務。
以上過程就實現了,從主執行緒進入子執行緒處理耗時問題,到處理完成後返回主執行緒的過程。
定時器執行緒例項
接下來看程式碼,有點長。。。
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
void MyWork(); //執行緒處理函式定義
void setFlag(bool flag = true);
signals:
void signal_back();//處理結果返回訊號
private:
bool isStop;
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QThread>
#include <QDebug>
MyThread::MyThread(QObject *parent) : QObject(parent)
{
isStop = false;
}
void MyThread::MyWork() //執行緒處理函式:具體處理的事情
{
while(!isStop)
{
QThread::sleep(1);
emit signal_back(); //傳送返回訊號
qDebug() << "the child thread number:" << QThread::currentThread();
}
}
void MyThread::setFlag(bool flag)
{
isStop = flag;
}
mywidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
#include "mythread.h"
#include <QThread>
namespace Ui {
class MyWidget;
}
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
~MyWidget();
void slot_handle_finish();
signals:
void StartThread();
private slots:
void on_BtnStart_clicked();
void on_BtnStop_clicked();
private:
Ui::MyWidget *ui;
MyThread * m_MyThread;
QThread * subthread;
void CloseWidget();
};
#endif // MYWIDGET_H
mywidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
#include <QDebug>
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
//creat a child thread
subthread = new QThread(this);
//creat a new object m_MyThread
m_MyThread = new MyThread();
//move the new object to child thread
m_MyThread->moveToThread(subthread);
connect(this, &MyWidget::StartThread, m_MyThread, &MyThread::MyWork);
connect(m_MyThread, &MyThread::signal_back, this, &MyWidget::slot_handle_finish);
qDebug() << "the main thread number:" << QThread::currentThread();
connect(this, &MyWidget::destoryed, this, &MyWidget::CloseWidget); //左上角視窗關閉
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::on_BtnStart_clicked()
{
if(subthread->isRunning() == true) return;
m_MyThread->setFlag(false);
//start the child thread
subthread->start();
//啟動了執行緒,但是並沒有進入執行緒
//必須通過訊號/槽的方式進入子執行緒
//直接通過m_MyThread->Mywork()是不行的,這樣MyWork()中的執行緒就是主執行緒
emit StartThread();
}
void MyWidget::slot_handle_finish()
{
static int i = 0;
i++;
ui->lcdNumber->display(i);
}
void MyWidget::on_BtnStop_clicked()
{
if(subthread->isRunning() == true) return;
m_MyThread->setFlag();
subthread->quit();
subthread->wait();
}
void MyWidget::CloseWidget()
{
m_MyThread->setFlag();
subthread->quit();
subthread->wait();
delete m_MyThread;
}
以上過程就啟動執行緒,即通過開始按鈕,開始計時
注:
1、子執行緒的處理函式中不能操作圖形介面,只能進行資料處理,因為是後臺執行
2、connect函式的第5個引數ConnectionType的問題
- ConnectionType主要有三種Qt::AutoConnection(預設)、Qt::DirectConnection(直接)、Qt::QueuedConnection(佇列)
- Qt::AutoConnecdann為預設預設引數,單執行緒時->Qt::DirectConnection,多執行緒時->Qt::QueuedConnection
- 如果在多執行緒時,若強制設定引數為Qt::DirectConnection,那麼多執行緒將失效,子執行緒的處理函式仍在主執行緒中
- 所以才需要通過connect來實現多執行緒,因為其內部已經有這樣的一個實現多執行緒的機制
- 佇列:槽函式所線上程和接收者一樣
- 直接:槽函式所線上程和傳送者一樣