1、線程開啓
Qt中,開啓子線程,一般有兩種方法:
a, 定義工作類worker:
worker繼承 QThread, 重寫run函數,在主線程中實例化worker,把耗時工作放進worker的run函數中完成,結束後,往主線程中發信號,傳遞參數即可。
注意:此worker的實例,只有run函數在子線程中執行,worker的其他函數,均在主線程中執行。
如果子線程已經start開啓,run函數尚未運行完時,再次start,此時子線程不會有任何操作,run函數不會被重新調用,會繼續執行run函數。
b, 定義工作類worker:
worker繼承Qobject,在worker中完成耗時操作,並在主線程中 #include "worker.h"進來,隨後,在主線程中New出幾個子線程QThread,使用moveToThread()函數,把worker轉移進入子線程,例如:
pWorker = new Worker;
pThread1 = new QThread;
pWorker->moveToThread(pThread1)
之後,再根據需求,看何時開啓子線程:pThread1->start();
如果線程已經運行,你重複調用start其實是不會進行任何處理。
2、線程關閉
對於上面a類,在run中開啓的子線程,如果run中沒有調用exec(),使用quit(),exit(),是無法跳出run中的循環,終止子線程的。不會發生任何效果,QThread不會因爲你調用quit()函數而退出正在運行到一半的run。
但使用QThread的terminate()方法,可以立刻結束子線程,但這個函數存在非常不安定因素,不推薦使用。那麼如何安全的終止一個線程呢?
最簡單的方法是添加一個bool變量,通過主線程修改這個bool變量來進行終止,但這樣有可能引起訪問衝突,需要對其進行加鎖。
void myThread::run()
{
int count = 0;
m_isCanRun = true;//標記可以運行
QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__)
.arg((unsigned int)QThread::currentThreadId());
emit message(str);
while(1)
{
sleep(1);
++count;
doSomething();
if(m_runCount == count)
{
break;
}
{
QMutexLocker locker(&m_lock);// 此處加鎖,防止訪問衝突
if(!m_isCanRun)//在每次循環判斷是否可以運行,如果不行就退出循環
{
return;
}
}
}
}
因此在子線程的run函數的循環中遇到m_isCanRun的判斷後就會退出run函數,繼承QThread的函數在運行完run函數後就視爲線程完成,會發射finish信號。
子線程指針,儘量不要去delete ,這樣不安全。一般會綁定QObject::deleteLater()方法。
connect(pThread,&QThread::finished ,thread,&QObject::deleteLater);
線程結束後調用deleteLater來銷燬分配的內存。
對於上面b類,在Qt4.8之後,Qt多線程的寫法最好還是通過QObject來實現,和線程的交互通過信號和槽(實際上其實是通過事件)聯繫。
繼承QObject多線程的方法線程的創建很簡單,只要讓QThread的start函數運行起來就行,但是需要注意銷燬線程的方法: 在線程創建之後,這個QObject的銷燬不應該在主線程裏進行,而是通過deleteLater槽進行安全的銷燬,因此,繼承QObject多線程的方法在創建時有幾個槽函數需要特別關注:
- 一個是QThread的finished信號對接QObject的deleteLater使得線程結束後,繼承QObject的那個多線程類會自己銷燬
- 另一個是QThread的finished信號對接QThread自己的deleteLater,這個不是必須,下面官方例子就沒這樣做:
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
使用QObject創建多線程的方法如下:
- 寫一個繼承QObject的類,對需要進行復雜耗時邏輯的入口函數聲明爲槽函數
- 此類在舊線程new出來,不能給它設置任何父對象
- 同時聲明一個QThread對象,在官方例子裏,QThread並沒有new出來,這樣在析構時就需要調用- – QThread::wait(),如果是堆分配的話, 可以通過deleteLater來讓線程自殺
- 把obj通過moveToThread方法轉移到新線程中,此時object已經是在線程中了
- 把線程的finished信號和object的deleteLater槽連接,這個信號槽必須連接,否則會內存泄漏
- 正常連接其他信號和槽(在連接信號槽之前調用moveToThread,不需要處理connect的第五個參數,否則就顯示聲明用Qt::QueuedConnection來連接)
- 初始化完後調用’QThread::start()'來啓動線程
- 在邏輯結束後,調用QThread::quit退出線程的事件循環
3、QMutex線程加鎖
QMutex類提供的是多線程之間的訪問順序化。QMutex的目的是保護一個對象、函數、數據結構或者代碼段,所以同一時間只有一個線程可以訪問它。
例如:
void myTest::testOut()
{
//mtex.lock();
_sleep(3000);
qDebug() << "this is mytest1: -----" ;
qDebug() << "this is mytest2:----";
//mtex.unlock();
}
如果同時在兩個線程中調用這個方法,結果的順序將是:
this is mytest1: -----
this is mytest1: -----
this is mytest2: -----
this is mytest2: -----
如果你使用互斥量,加了鎖之後:
void myTest::testOut()
{
mtex.lock();
_sleep(3000);
qDebug() << "this is mytest1: -----" ;
qDebug() << "this is mytest2:----";
mtex.unlock();
}
輸出結果爲:
this is mytest1: -----
this is mytest2: -----
// 中間延遲 3000ms
this is mytest1: -----
this is mytest2: -----
一個 使用run(),moveToThread()新建子線程並結束線程,QMutex以及單例化實現的Demo示例:
myMutex1::myMutex1(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
t1 = new myThread();
t2 = new myThread();
t3 = new myThread();
t4 = new QThread();
t5 = new QThread();
t6 = new QThread();
my1 = myTest::getInstance();
my2 = myTest::getInstance();
my3 = myTest::getInstance();
my1->moveToThread(t4);
my2->moveToThread(t5);
my3->moveToThread(t6);
qDebug() << "t1 addr----" << t1;
qDebug() << "t2 addr----" << t2;
qDebug() << "t3 addr----" << t3;
qDebug() << "t4 addr----" << t4;
qDebug() << "t5 addr----" << t5;
qDebug() << "t6 addr----" << t6;
}
該樣例Demo下載地址:
https://download.csdn.net/download/birenxiaofeigg/11963711