文章目錄
- 一、Qt創建線程的三種方法
- 二、Qt并發:QtConcurrent介紹
- 三、QtConcurrent run參數說明
- 四、獲取QtConcurrent的返回值
- 五、C++其他線程技術介紹
一、Qt創建線程的三種方法
??以下是Qt創建線程的三種方法:
-
方法一:派生于QThread
派生于QThread,這是Qt創建線程最常用的方法。線程類中重寫虛函數
void QThread::run();
,在run()
函數中寫具體內容,外部通過start()
調用,即可執行線程體run()
。注意:派生于QThread的類,構造函數屬于主線程,run函數屬于子線程,可以通過打印線程id來判斷
示例:
VS中創建Qt控制臺項目
MyThread.h:
#pragma once#include <QThread>class MyThread : public QThread { public:MyThread();void run()override; };
MyThread.cpp:
#include "MyThread.h" #include <QDebug>MyThread::MyThread() {qDebug() << "MyThread construct id =" << QThread::currentThreadId; }void MyThread::run() {qDebug() << "MyThread run id =" << QThread::currentThreadId;int index = 0;while (1){qDebug() << index++;QThread::msleep(500);} }
main.cpp:
#include <QtCore/QCoreApplication> #include <QDebug> #include "MyThread.h"int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);qDebug() << "main thread id = " << QThread::currentThreadId;MyThread th;th.start();qDebug() << "main thread end!";return a.exec(); }
運行結果:
main thread id = 0x78272815 MyThread construct id = 0x78272815 main thread end! MyThread run id = 0x78272815 0 1 2 3 4 5 6 ...
-
方法二:派生于QRunnable
派生于QRunnable,重寫run()方法,在run()方法里處理其他任務,調用時需要借助Qt線程池
注意:這種新建線程的方法的最大缺點是:不能使用Qt信號槽機制,因為QRunnable不是繼承自QObject。但是這種方法的好處是:可以讓QThreadPool來管理線程,QThreadPool會自動清理我們新建的QRunnable對象。
示例:
MyRunnable.h:
#pragma once#include <QRunnable>class MyRunnable : public QRunnable { public:MyRunnable();void run()override; };
MyRunnable.cpp:
#include "MyRunnable.h" #include <QThread> #include <QDebug>MyRunnable::MyRunnable() {qDebug() << "MyRunnable construct id =" << QThread::currentThreadId; }void MyRunnable::run() {qDebug() << "MyRunnable run id =" << QThread::currentThreadId; }
main.cpp:
#include <QtCore/QCoreApplication> #include <QDebug> #include <QThreadPool> #include "MyThread.h" #include "MyRunnable.h"int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);qDebug() << "main thread id = " << QThread::currentThreadId;//MyThread th;//th.start();MyRunnable* th = new MyRunnable();QThreadPool::globalInstance()->start(th);qDebug() << "main thread end!";return a.exec(); }
運行結果:
main thread id = 0x78272815 MyRunnable construct id = 0x78272815 main thread end! MyRunnable run id = 0x78272815
-
方法三:moveToThread
派生于QObject,使用moveToThread方法將任何QObject派生類的實例移動到另一個線程。將QThread對象作為私有成員,在構造函數里moveToThread,然后啟動線程。
QThread *thread = new QThread; this->moveToThread(thread); thread->start();
示例:
MyObject.h:
#pragma once #include <qobject.h> #include <QThread>class MyObject : public QObject {Q_OBJECT public:MyObject();public slots:void process();private:QThread m_pTh; };
MyObject.cpp:
#include "MyObject.h" #include <QDebug>MyObject::MyObject() {this->moveToThread(&m_pTh);m_pTh.start();qDebug() << "MyObject construct id = " << QThread::currentThreadId(); }void MyObject::process() {qDebug() << "MyObject process id = " << QThread::currentThreadId();int index = 0;while (1){qDebug() << index++;QThread::msleep(300);} }
ch7_1_mtt.h:
#pragma once#include <QtWidgets/QWidget> #include "ui_ch7_1_mtt.h" #include "MyObject.h"class ch7_1_mtt : public QWidget {Q_OBJECTpublic:ch7_1_mtt(QWidget *parent = nullptr);~ch7_1_mtt();public slots:void on_pushButton_clicked();signals:void sig_pro();private:Ui::ch7_1_mttClass ui;MyObject* m_pObj; };
ch7_1_mtt.cpp:
#include "ch7_1_mtt.h" #include <QDebug>ch7_1_mtt::ch7_1_mtt(QWidget *parent): QWidget(parent) {ui.setupUi(this);qDebug() << "main construct id = " << QThread::currentThreadId();m_pObj = new MyObject();connect(this, &ch7_1_mtt::sig_pro, m_pObj, &MyObject::process); }ch7_1_mtt::~ch7_1_mtt() {}void ch7_1_mtt::on_pushButton_clicked() {//通過信號槽方式調用,相當于在子線程中運行;若直接調用process()函數,則相當于在主線程中運行emit sig_pro(); }
在槽函數中進行耗時操作可以使用線程,在子線程中進行耗時操作
main.cpp:
#include "ch7_1_mtt.h" #include <QtWidgets/QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);ch7_1_mtt w;w.show();return a.exec(); }
運行結果:
main construct id = 0x52dc MyObject construct id = 0x52dc MyObject process id = 0x78272815 0 1 2 3 4 5 6 7
二、Qt并發:QtConcurrent介紹
??QtConcurrent是Qt框架中的一個模塊,用于簡化并發和多線程編程。它提供了一種高效的方式來執行并行任務,而無需直接處理線程的復雜性。QtConcurrent基于模板和函數式編程,使得編寫并發代碼更加簡潔和易于理解。以下是QtConcurrent的一些關鍵特性:
- 基于模板的并行算法:QtConcurrent提供了一系列的并行算法,如
QtConcurrent::map()
、QtConcurrent::filter()
和QtConcurrent::reduce()
等,它們可以并行地對數據集執行操作。 - 基于Qt的線程池管理:QtConcurrent使用Qt的線程池來管理線程,這意味著開發者不需要手動創建和銷毀線程,從而簡化了線程管理。
- 信號和槽的集成:QtConcurrent可以與Qt的信號和槽機制無縫集成,使得并行任務的結果可以很容易地通過信號傳遞給其他對象。
- 懶加載和任務取消:QtConcurrent支持懶加載,即任務只有在實際需要結果時才開始執行。此外,它還支持取消正在執行的任務。
- 異常處理:QtConcurrent可以處理并行執行過程中拋出的異常,確保程序的健壯性。
- 無阻塞的等待:QtConcurrent提供了一種機制來等待并行任務的完成,而不會阻塞調用線程。
Header:
#include <QtConcurrent>
qmake:
QT += concurrent
QtConcurrent基本用法:
??QtConcurrent::run
這行代碼的作用是啟動ch72_concurrent
類的timeTask
成員函數在單獨的線程中異步執行,并且返回一個QFuture<int>
對象,用于跟蹤執行的狀態和獲取返回值。
QFuture<int> ft = QtConcurrent::run(this, &ch72_concurrent::timeTask);while (!ft.isFinished())
{//用于處理事件隊列中的事件的。這個函數可以確保應用程序界面保持響應狀態,即使在執行長時間運行的任務時//當future未完成時,讓cpu去做別的事QApplication::processEvents(QEventLoop::AllEvents, 30);
}
示例:
xx.h:
#pragma once#include <QtWidgets/QWidget>
#include "ui_ch72_concurrent.h"class ch72_concurrent : public QWidget
{Q_OBJECTpublic:ch72_concurrent(QWidget *parent = nullptr);~ch72_concurrent();int timeTask();private slots:void on_pushButton_clicked();private:Ui::ch72_concurrentClass ui;
};
xx.cpp:
#include "ch72_concurrent.h"
#include <QThread>
#include <QDebug>
#include <QtConcurrent>
#include <QFuture>ch72_concurrent::ch72_concurrent(QWidget *parent): QWidget(parent)
{ui.setupUi(this);
}ch72_concurrent::~ch72_concurrent()
{}int ch72_concurrent::timeTask()
{int num = 0;for (int i = 0; i < 1000000; i++){num++;qDebug() << num;}return num;
}void ch72_concurrent::on_pushButton_clicked()
{//timeTask();QFuture<int> ft = QtConcurrent::run(this, &ch72_concurrent::timeTask);while (!ft.isFinished()){//用于處理事件隊列中的事件的。這個函數可以確保應用程序界面保持響應狀態,即使在執行長時間運行的任務時QApplication::processEvents(QEventLoop::AllEvents, 30); }
}
main.cpp:
#include "ch72_concurrent.h"
#include <QtWidgets/QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);ch72_concurrent w;w.show();return a.exec();
}
三、QtConcurrent run參數說明
??QtConcurrent run函數參數,可以是全局函數,也可以是類成員函數。
-
類成員函數做run參數
int ch73_concurrent::timeTask(int num1, int num2) {//int num = 0;for (int i = 0; i < 1000000; i++){num1++;num2++;qDebug() << num1;qDebug() << num2;}return num1 + num2; }void ch73_concurrent::on_pushButton_clicked() {//timeTask();int num1 = 0;int num2 = 0;QFuture<int> ft = QtConcurrent::run(this, &ch73_concurrent::timeTask, num1, num2);while (!ft.isFinished()){QApplication::processEvents(QEventLoop::AllEvents, 30);} }
-
全局函數做run參數
static int gTimeTask(int num1, int num2) {//int num = 0;for (int i = 0; i < 1000000; i++){num1++;num2++;qDebug() << num1;qDebug() << num2;}return num1 + num2; }void ch73_concurrent::on_pushButton_clicked() {//timeTask();int num1 = 0;int num2 = 0;//QFuture<int> ft = QtConcurrent::run(this, &ch73_concurrent::timeTask, num1, num2);QFuture<int> ft = QtConcurrent::run(gTimeTask, num1, num2);while (!ft.isFinished()){QApplication::processEvents(QEventLoop::AllEvents, 30);} }
四、獲取QtConcurrent的返回值
??獲取QtConcurrent的結果,需要使用QFutureWatcher類,鏈接它的信號finished,然后給watcher設置future,當監控到future執行結束后,可以獲取它的執行結果,調用的是result()函數。
int ch74::timeTask(int& num1, int& num2)
{for (int i = 0; i < 1000; i++){num1++;num2++;qDebug() << num1;qDebug() << num2;}return num1 + num2;
}void ch74::on_pushButton_clicked()
{int num1 = 0;int num2 = 0;QFutureWatcher<int>* fw = new QFutureWatcher<int>;connect(fw, &QFutureWatcher<int>::finished, [&]{qDebug() << "QFutureWatcher finished";qDebug() << "result = " << fw->result();});QFuture<int> ft = QtConcurrent::run(this, &ch74::timeTask, num1, num2);fw->setFuture(ft);while (!ft.isFinished()){QApplication::processEvents(QEventLoop::AllEvents, 30);}
}
五、C++其他線程技術介紹
- pthread:linux線程
- 這是POSIX線程庫,主要用于Unix-like系統,如Linux和macOS。
- 提供了豐富的線程創建和管理功能,包括互斥鎖、條件變量等同步機制。
- win32-pthread,obs的線程全部使用了win32-pthread
- 這是一個在Windows平臺上模擬POSIX線程庫的庫。
- OBS(Open Broadcaster Software)等應用程序使用這個庫來實現跨平臺的線程功能。
- windows thread類
- Windows API提供了自己的線程類,如
Win32 Thread
,用于創建和管理線程。 - 這些線程類通常與Windows特定的功能緊密結合,如窗口消息處理。
- Windows API提供了自己的線程類,如
- MFC thread類
- MFC(Microsoft Foundation Classes)是一套C++庫,用于Windows應用程序開發。
- MFC提供了自己的線程類,例如
CWinThread
,用于簡化線程的創建和管理。
- boost
- Boost庫是一個廣泛使用的C++庫集合,其中包含了線程庫。
- Boost.Thread提供了跨平臺的線程支持,包括線程創建、同步等。
- std::thread(推薦使用這個,基于語言級別的跨平臺C++線程)
- C++11標準引入了
std::thread
,這是語言級別的線程支持。 - 推薦使用
std::thread
,因為它提供了跨平臺的線程支持,并且與C++標準庫緊密集成。 std::thread
簡化了線程的創建和管理,同時提供了豐富的同步機制,如std::mutex
、std::condition_variable
等。
- C++11標準引入了