欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Qt 中開啟線程的多種方式小結(jié)

 更新時間:2021年09月02日 11:07:54   作者:lucky-billy  
本篇文章就來整理一下 Qt 中使用線程的五種方式,方便后期回顧。前面兩種比較簡單,一筆帶過了,主要介紹后面三種,感興趣的朋友跟隨小編一起看看吧

簡介

在開發(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)文章

最新評論