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訊號。
以上是學習執行緒的一些筆記,如有不足,希望多多指正。