VS+QT多執行緒實作——run和moveToThread
- 實作方法及特性
- 多執行緒run的實作
- 1.代碼
- 2.效果
- moveToThread代碼實作
- 1.代碼
- 2.效果
- 討論
- 工程原始碼
- 參考資料
寫在前頭:最近在學習多執行緒,以小白的視角寫一些學習心得,也歡迎各位朋友勘誤補充,
開發環境:VS2013+QT5
實作方法及特性
- run ——繼承QThread的run函式,通過重寫run()方法,實作任務功能,
- 使用run方便理解,簡單的任務流程可以封裝在run里面,
- run 是執行緒的入口,run的開始和結束意味著執行緒的開始和結束,
- 多執行緒訪問變數或處理事務要考慮加鎖,(目前還未涉及加鎖,不展開討論)
- 執行緒start()起來,理論上run函式會從頭執行到結束,所以如果run中的任務是多步驟,中途需要資料反饋或ui變化,做例外處理相對麻煩,
- QThread只有run函式是在新執行緒里的,其他所有函式都在QThread生成的執行緒里!!!
- moveToThread——用moveToThread將繼承于QObject的類轉移到Thread里,
- 通過信號與槽連接,幾乎不用考慮多執行緒的存在,也不用考慮使用QMutex來進行同步,
- QT4.8之后,QT官方建議使用此方法,
多執行緒run的實作
1.代碼
myThread.h檔案
#pragma once
#include <QThread>
class myThread :public QThread
{
Q_OBJECT
public:
myThread();
~myThread();
private:
void run();
};
myThread.CPP檔案
#include "myThread.h"
#include <QDebug>
myThread::myThread()
{
}
myThread::~myThread()
{
}
//重寫run函式
void myThread::run()
{
qDebug() << "currentThreadId is" << QThread::currentThreadId();
//Todo此處重寫功能,此處用了for回圈代替,可以根據需求定義功能
for (int i = 0; i < 100;i++)
{
;
}
qDebug() << "currentThreadId " << QThread::currentThreadId()<<"run to end";
}
RunDemo.h檔案
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_RunDemo.h"
#include "myThread.h"
#include <QtDebug>
class RunDemo : public QMainWindow
{
Q_OBJECT
public:
RunDemo(QWidget *parent = Q_NULLPTR);
~RunDemo();
private slots:
void on_btnOK_Clicked();
private:
Ui::RunDemoClass ui;
myThread *m_thread;
};
RunDemo.cpp檔案
#include "RunDemo.h"
RunDemo::RunDemo(QWidget *parent): QMainWindow(parent)
{
ui.setupUi(this);
//實體化
m_thread = new myThread;
//可以在QT Designer中連接,
connect(ui.btnOK, SIGNAL(clicked()), this, SLOT(on_btnOK_Clicked()));
}
RunDemo::~RunDemo()
{
if (m_thread)
{
delete m_thread;
m_thread = NULL;
}
}
void RunDemo::on_btnOK_Clicked()
{
qDebug() << "MainThreadId is" << QThread::currentThreadId();
m_thread->start();
ui.lineEdit->setText("hello world");
}
2.效果
子執行緒里做了回圈操作,沒有執行任何任務,lineEdit文字顯示為按鍵槽函式執行的結果,

moveToThread代碼實作
1.代碼
Woker.h檔案
#pragma once
#include "qobject.h"
class Woker :public QObject
{
Q_OBJECT
public:
Woker();
~Woker();
public slots:
void doWork(int i);
signals:
void SendToMain(int i);
};
Woker.cpp檔案
#include "Woker.h"
#include <QDebug>
#include <QThread>
Woker::Woker()
{
}
Woker::~Woker()
{
}
//執行緒需要執行的任務
void Woker::doWork(int i)
{
qDebug() << "currentThreadId is" << QThread::currentThreadId();
//Todo此處為執行的任務功能,用了累加(+1)代替,可按需定義自己的功能
i++;
//發送信號
emit SendToMain(i);
qDebug() << "currentThreadId " << QThread::currentThreadId() << "run to end";
}
moveToThreadDemo.h檔案
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_moveToThreadDemo.h"
#include <QThread>
#include "Woker.h"
#include <QString>
#include <QDebug>
class moveToThreadDemo : public QMainWindow
{
Q_OBJECT
public:
moveToThreadDemo(QWidget *parent = Q_NULLPTR);
private:
Ui::moveToThreadDemoClass ui;
signals:
//為方便理解,這里傳遞的引數為int數字,可以根據需求自定義型別
//傳遞的引數也可以是結構體、類物件,專案中我傳遞了串口(指標)
void SendToWoker(int i);
public slots:
void on_btnOK_Clicked();
void handleResult(int i);
};
moveToThreadDemo.cpp檔案
#include "moveToThreadDemo.h"
moveToThreadDemo::moveToThreadDemo(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
//按鍵功能連接
connect(ui.btnOK, SIGNAL(clicked()), this, SLOT(on_btnOK_Clicked()));
//宣告任務woker
Woker *myWoker = new Woker;
//建立執行緒
QThread *mythread = new QThread;
connect(this, SIGNAL(SendToWoker(int)), myWoker, SLOT(doWork(int)));
connect(myWoker, SIGNAL(SendToMain(int)), this, SLOT(handleResult(int)));
connect(mythread, &QThread::finished, myWoker, &QObject::deleteLater);
myWoker->moveToThread(mythread);
mythread->start();
}
void moveToThreadDemo::on_btnOK_Clicked()
{
qDebug() << "MainThreadId is" << QThread::currentThreadId();
//將數字1作為信號傳遞出去
emit SendToWoker(1);
}
//處理函式
void moveToThreadDemo::handleResult(int i)
{
//接收woker傳遞過來的i,并進行顯示
QString qstr = QString("%1").arg(i);
ui.lineEdit->setText(qstr);
}
2.效果
這里子執行緒將收到的數字1做了累加處理,并將結果傳回主執行緒進行顯示,

討論
在實際操作的時候,發現moveToThread的方法確實有其獨特的優勢,但在實際應用場景也出現了一個問題,希望各位朋友一起來討論討論,
需求是多執行緒多串口的開發,由于串口數量不定,所以在執行緒建立,連接的程序中,后面的信號也會把前面的執行緒中槽函式觸發,可以理解為在宣告的時候,加了一個for回圈,這里的回圈次數就是串口的數量,

從運行結果來看,一共跑了6次任務,原因是第1個sendToWorker把第1個doWork(int)觸發,由于是回圈做的connect,第2個sendToWorker會把第1個和第2個doWork(int)觸發,第3個sendToWorker會把第1個、第2個和第3個的doWork(int)觸發,這就造成了系統資源的浪費,本意上是希望第i個sendToWorker只觸發第i個doWork(int),

目前的做法是在woker類里,給woker加入了私有變數ID,傳遞引數中也增加了ID,通過判斷ID是否一致來決定是否執行,但是,不清楚是不是有更好的辦法,解決方案如下:
Woker.h檔案
#pragma once
#include "qobject.h"
class Woker :public QObject
{
Q_OBJECT
public:
Woker(int ID);
~Woker();
private:
int _wokerID;//加入私有ID
public slots:
void doWork(int i);
signals:
void SendToMain(int i);
};
Woker.cpp檔案
#include "Woker.h"
#include <QDebug>
#include <QThread>
Woker::Woker( int ID)
{
_wokerID = ID;
}
Woker::~Woker()
{
}
void Woker::doWork(int i)
{
//增加ID判斷
if (i!=_wokerID)
{
qDebug() << "ThreadId" << QThread::currentThreadId() << "return";
return;
}
qDebug() << "currentThreadId is" << QThread::currentThreadId();
//Todo此處重寫功能,此處用了+1功能代替
i++;
//發送信號
emit SendToMain(i);
qDebug() << "currentThreadId " << QThread::currentThreadId() << "run to end";
}

從結果可以看出,實際只跑了3次任務,不相干的觸發信號被return,從而解決多次觸發,多次執行的問題,這個方法在使用的時候也要注意,任務復位后需要disconnect,不然重復執行動作,會出現多個同ID的woker,我這里用串口號(COM3/COM4/COM5)作為woker的ID,實際場景就是串口初始化,進行通信,關閉串口需要disconnect,不然再次使用該串口,進行初始化的時候,當前連接的信號會觸發前幾次初始化連接的槽函式,此時ID是相同的,所以在串口關閉的時候,需要disconnect當前的連接,

工程原始碼
- RunDemo下載
- moveToThreadDemo下載
參考資料
- QThread使用——關于run和movetoThread的區別
- Qt執行緒—QThread的使用–run和movetoThread的用法
- Qt執行緒實作分析-moveToThread vs 繼承
- Qt多執行緒中的moveToThread()的簡單用法
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/35836.html
標籤:其他
上一篇:詞法分析程式
