Qt 中開啟線程的多種方式小結(jié)
簡介
在開發(fā)過程中,使用線程是經(jīng)常會遇到的場景,本篇文章就來整理一下 Qt 中使用線程的五種方式,方便后期回顧。前面兩種比較簡單,一筆帶過了,主要介紹后面三種。最后兩種方法博主最喜歡,不需要繼承類,可以直接把需要執(zhí)行的函數(shù)放到線程中去運行
1. 繼承 QThread 重寫 run 函數(shù)
class Thread : public QThread { Q_OBJECT public: virtual void run() override; }
void Thread::run() { ... }
- 可調(diào)用
thread.start()
啟動線程,會自動調(diào)用 run 函數(shù) - 可調(diào)用
thread.isRunning()
判斷線程是否已啟動 - 可調(diào)用
thread.terminate()
終止線程 - 可調(diào)用
thread.wait()
等待線程終止
2. 繼承 QObject 調(diào)用 moveToThread
class Test : public QObject { Q_OBJECT public: void test(); }
QThread th; Test test; test.moveToThread(&th);
需要注意的是:此方法與繼承 QThread 相比較,繼承 QThread 只有 run 函數(shù)中的操作是在線程中執(zhí)行的,而此方法中所有的成員函數(shù)都是在線程中執(zhí)行
3. 繼承 QRunnable 重新 run 函數(shù),結(jié)合 QThreadPool 實現(xiàn)線程池
#include <QObject> #include <QRunnable> #include <QThread> #include <QThreadPool> #include <QDebug> class BPrint : public QRunnable { void run() { for ( int count = 0; count < 5; ++count ) { qDebug() << QThread::currentThread(); QThread::msleep(1000); } } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QThreadPool threadpool; // 構(gòu)建一個本地的線程池 threadpool.setMaxThreadCount(3); // 線程池中最大的線程數(shù) for ( int num = 0; num < 100; ++num ) { BPrint *print; // 循環(huán)構(gòu)建可在線程池中運行的任務 threadpool.start(print); // 線程池分配一個線程運行該任務 QThread::msleep(1000); } return a.exec(); }
在上述例子當中,我們創(chuàng)建的 QRunnable 類型的指針 BPrint *print 是不需要我們手動去回收內(nèi)存的,QThreadPool 在結(jié)束該任務的執(zhí)行后會將對該內(nèi)存進行清空
有的小伙伴會有疑問,既然有 QThread 線程類了,為啥還要用 QRunnable + QThreadPool 創(chuàng)建線程池的方法來使用線程機制呢,感覺用起來很麻煩啊。所以這里要說明一下此方法的使用場景,當線程任務量非常大的時候,如果頻繁的創(chuàng)建和釋放 QThread 會帶來非常大的內(nèi)存開銷,而線程池則可以有效避免這個問題
還有一個問題需要注意一下,QThread 是集成自 QObject 的,我們通常會使用信號槽與外界進行通信。而 QRunnable 并不是繼承自 QObject 類的,所以他無法使用信號槽機制進行通信。這里推薦兩種方法,一個是使用 QMetaObject::invokeMethod()
函數(shù)。另一個是使用多重繼承的方法,自定義類需要同時繼承自 QRunnable 和 QObject
4. 使用 C++ 11 中的 sth::thread
#include <thread> void threadfun1() { std::cout << "threadfun1 - 1\r\n" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "threadfun1 - 2" << std::endl; } void threadfun2(int iParam, std::string sParam) { std::cout << "threadfun2 - 1" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(5)); std::cout << "threadfun2 - 2" << std::endl; } int main() { std::thread t1(threadfun1); std::thread t2(threadfun2, 10, "abc"); t1.join(); // 等待線程 t1 執(zhí)行完畢 std::cout << "join" << std::endl; t2.detach(); // 將線程 t2 與主線程分離 std::cout << "detach" << std::endl; } 運行結(jié)果: threadfun1 - 1 threadfun2 - 1 threadfun1 - 2 join detach
根據(jù)輸出結(jié)果可以得知,t1.join() 會等待t1線程退出后才繼續(xù)往下執(zhí)行,t2.detach() 并不會等待,detach字符輸出后,主函數(shù)退出,threadfun2還未執(zhí)行完成,但是在主線程退出后,t2的線程也被已經(jīng)被強退出
5. Qt QtConcurrent 之 Run 函數(shù)
Concurrent 是并發(fā)的意思,QtConcurrent 是一個命名空間,提供了一些高級的 API,使得所寫的程序可根據(jù)計算機的 CPU 核數(shù),自動調(diào)整運行的線程數(shù)目。這意味著今后編寫的應用程序?qū)⒃谖磥聿渴鹪诙嗪讼到y(tǒng)上時繼續(xù)擴展
函數(shù)原型如下:
QFuture<T> QtConcurrent::run(Function function, ...)
QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)
簡單來說,QtConcurrent::run() 函數(shù)會在一個單獨的線程中執(zhí)行,并且該線程取自全局 QThreadPool,該函數(shù)的返回值通過 QFuture API 提供
需要注意的是:
1)該函數(shù)可能不會立即運行; 函數(shù)只有在線程可用時才會運行
2)通過 QtConcurrent::run() 返回的 QFuture 不支持取消、暫停,返回的 QFuture 只能用于查詢函數(shù)的運行/完成狀態(tài)和返回值
3) Qt Concurrent 已經(jīng)從 QtCore 中移除并成為了一個獨立的模塊,所以想要使用 QtConcurrent 需要在 pro 文件中導入模塊:
QT += concurrent
使用方式有以下幾種:
1)將函數(shù)運行在某一個線程中,需要使用 extern
extern void func(); QFuture<void> future = QtConcurrent::run(func);
2)向該函數(shù)傳遞參數(shù)
extern void FuncWithArguments(int arg1, const QString &string); int integer = ...; QString string = ...; // 需要傳遞的參數(shù),則跟在函數(shù)名之后,依次加入 QFuture<void> future = QtConcurrent::run(FuncWithArguments, integer, string);
3) 獲取該函數(shù)的計算結(jié)果
extern QString Func(const QByteArray &input); QByteArray byte_array = ...; QFuture<QString> future = QtConcurrent::run(func, byte_array); ... QString result = future.result();
4)常量成員函數(shù)
QByteArray bytearray = "hello world"; // 在一個單獨的線程中,調(diào)用 QByteArray 的常量成員函數(shù) split(),傳遞給 run() 函數(shù)的參數(shù)是 bytearray QFuture< QList<QByteArray> > future = QtConcurrent::run(bytearray, &QByteArray::split, ','); ... QList<QByteArray> result = future.result();
5)非常量成員函數(shù)
QImage image = ...; // 在一個單獨的線程中,調(diào)用 QImage 的非常量成員函數(shù) invertPixels(),傳遞給 run() 函數(shù)的參數(shù)是 &image QFuture<void> future = QtConcurrent::run(&image, &QImage::invertPixels, QImage::InvertRgba); ... future.waitForFinished();
6)Lambda 表達式
#include <QFuture> #include <QtConcurrent> #include <QThreadPool> QThreadPool pool; QFuture<void> future = QtConcurrent::run(&pool, [&](QObject *receiver){ cv::Mat mat = QYImageProcessing::convertQImageToMat(image); cv::Mat center = cv::imread("dynaPhase_center.png"); dynaPhase_alive = QYImageProcessing::getDiffPoint(mat, center); // 根據(jù)三個點自適應模擬條紋 cv::Mat ret = DynamicCarrier::DC_Adaptive_Simulation(dynaPhase_center, dynaPhase_alive, dynaPhase_align); ret = ret*255; ret.convertTo(ret, CV_8UC1); QImage adaptive = QYImageProcessing::convertMatToQImage(ret); QYAlignControl *align = static_cast<QYAlignControl *>(receiver); align->callQmlGetAlivePoint(adaptive, dynaPhase_alive.x, dynaPhase_alive.y); }, this);
到此這篇關(guān)于Qt 中開啟線程的五種方式的文章就介紹到這了,更多相關(guān)Qt 線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++?OpenCV實現(xiàn)boxfilter方框濾波的方法詳解
box?filter的作用很簡單,即對局部區(qū)域求平均,并把值賦給某個點,一般我們賦給區(qū)域中心。本文將用C++實現(xiàn)boxfilter方框濾波,需要的可以了解一下2022-10-10輸入一個字符串,取出其中的整數(shù)(實現(xiàn)代碼)
輸入一個字符串,內(nèi)含所有數(shù)字和非數(shù)字字符。將其中連續(xù)的數(shù)字作為一個整數(shù),依次存放到一個數(shù)組中,統(tǒng)計共有多少個整數(shù),并輸出這些數(shù)2013-09-09