1. 程式人生 > >QThread學習筆記和一個多執行緒模板

QThread學習筆記和一個多執行緒模板

1、QThread用到的函式

start()函式:會在新的執行緒中執行run()函式的內容。

terminate()函式:可以終止執行緒。執行緒有可能馬上終止,也有可能不會馬上終止,這取決於作業系統的排程策略。在使用terminate()函式後使用wait()函式可保證執行緒終止(備註:在呼叫terminate()函式時,執行緒可能正在修改資料,這可能會讓執行緒沒有機會釋放一些資源,包括自身使用的空間、互斥鎖等,簡而言之,這個函式只有在必要的時候才使用)。

exec()函式:如果要線上程中啟動事件迴圈,需要在run()函式內呼叫exec(),以此來代替執行緒中的while(1)迴圈。

quit() 或者 exit()函式:

結束執行緒的事件迴圈,可正常終止執行緒。

wait()函式:一個會阻塞的函式。在呼叫完quit()後,QThread可能還沒有完全停止,此時如果delete 執行緒程式就會報錯。在執行quit()後,呼叫wait()來等待QThread子執行緒的結束(即從run()函式的返回),這樣就能保證在清除QThread時,其子執行緒是停止執行的。

finished()訊號:當執行緒結束執行,finished()訊號就會被髮出。當這個訊號被髮出時,所有的事件迴圈都已經停止了,也沒有任何事件需要執行,除了延遲刪除事件。這個訊號可以與QObject::deleteLater()槽函式相連,來釋放執行緒中的物件使用的空間。

2、QT5使用多執行緒的方法

        如果想使用多執行緒,我們可以繼承QThread類,在子類中重寫run函式。當主執行緒呼叫了start()函式之後,run函式中的程式將會在新的執行緒中執行。

        另一種方法是使用QObject類提供的函式:void QObject::moveToThread(QThread *targetThread),這個函式可以將QObject成員的事件處理(譬如說slots和events)放到另一個執行緒中執行。

注意的事項:

QT有些資源不支援跨執行緒操作,例如在一個QObject子類物件的建構函式中呼叫了其成員(QTimer)的start()函式,然後呼叫這個物件的moveToThread,將物件移動到另一個執行緒中,當執行緒結束並銷燬物件時就出錯了,提示:

QObject::killTimer: Timers cannot be stopped from another thread

QObject::~QObject: Timers cannot be stopped from another thread

3、一個使用多執行緒的程式設計模板

Task類的.h檔案

//task.h
#ifndef TASK_H
#define TASK_H

#include <QObject>
#include <QThread>
#include <QPointer>

class Task : public QObject
{
    Q_OBJECT
public:
    //可以按需要向建構函式傳入引數
    Task();
    //定義為虛解構函式,使得在用基類指標釋放派生類物件時,可以呼叫派生類的解構函式
    virtual ~Task();

    virtual void start();
    void stop();
    bool wait(unsigned long time = ULONG_MAX);

signals:
    //這裡可以按需要定義其他訊號

    void finished(Task *task);

private slots:
    virtual void run() = 0;
    void _thread_finished();

private:
    QPointer<QThread> _thread;
};

#endif // TASK_H

Task的.cpp檔案

#include "task.h"
#include <QDebug>

Task::Task()
{

}

Task::~Task()
{
    //防止使用者沒有呼叫stop就退出執行緒
    if(!_thread.isNull() && _thread->isRunning())
    {
        //取消執行緒結束時訊號與_child_finished()槽函式的連線
        QObject::disconnect(_thread, &QThread::finished, this, &Task::_thread_finished);
        //退出執行緒
        _thread->quit();
    }
}

void Task::start()
{
    //如果執行緒正在執行,則不進行任何操作
    if(!_thread.isNull() && _thread->isRunning()) { return; }
    //建立新的子執行緒,
    _thread = new QThread();
    //將事件處理移動到新執行緒中
    this->moveToThread(_thread);
    //連線執行緒啟動的訊號,線上程中執行任務
    QObject::connect(_thread, &QThread::started, this, &Task::run);
    //執行緒結束時執行_child_finished()
    QObject::connect(_thread, &QThread::finished, this, &Task::_thread_finished, Qt::UniqueConnection);
    //執行緒結束時銷燬物件
    QObject::connect(_thread, &QThread::finished, _thread, &Task::deleteLater);
    //啟動執行緒
    _thread->start();
}

void Task::stop()
{
    //停止子執行緒
    if(!_thread.isNull() && _thread->isRunning()) { _thread->quit(); }
}

bool Task::wait(unsigned long time)
{
    //等待子執行緒結束
    return _thread->wait(time);
}

void Task::_thread_finished()
{
    emit finished(this);
}

使用方法:

1、繼承Task類,重寫純虛擬函式run。

2、啟動執行緒呼叫start函式,當start函式被呼叫後,run函式將在新執行緒內執行,且Task子類內的所有事件也都會在新執行緒內執行。

3、結束執行緒時呼叫stop函式。執行緒結束後,物件會發送finished訊號。

以上是學習執行緒的一些筆記,如有不足,希望多多指正。