1. 程式人生 > >關於使用繼承QObject實現多執行緒的理解——Qt推薦的方法

關於使用繼承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來實現多執行緒,因為其內部已經有這樣的一個實現多執行緒的機制
  • 佇列:槽函式所線上程和接收者一樣
  • 直接:槽函式所線上程和傳送者一樣