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

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

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

簡(jiǎn)介

在開發(fā)過程中,使用線程是經(jīng)常會(huì)遇到的場(chǎng)景,本篇文章就來整理一下 Qt 中使用線程的五種方式,方便后期回顧。前面兩種比較簡(jiǎn)單,一筆帶過了,主要介紹后面三種。最后兩種方法博主最喜歡,不需要繼承類,可以直接把需要執(zhí)行的函數(shù)放到線程中去運(yùn)行

1. 繼承 QThread 重寫 run 函數(shù)

class Thread : public QThread
{
    Q_OBJECT
public:
	virtual void run() override;
}
void Thread::run()
{
	...
}
  • 可調(diào)用 thread.start()啟動(dòng)線程,會(huì)自動(dòng)調(diào)用 run 函數(shù)
  • 可調(diào)用 thread.isRunning()判斷線程是否已啟動(dòng)
  • 可調(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 實(shí)現(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)建一個(gè)本地的線程池
	threadpool.setMaxThreadCount(3);        // 線程池中最大的線程數(shù)
	
    for ( int num = 0; num < 100; ++num )
	{
	    BPrint *print;    					// 循環(huán)構(gòu)建可在線程池中運(yùn)行的任務(wù)
	    threadpool.start(print);      		// 線程池分配一個(gè)線程運(yùn)行該任務(wù)
	    QThread::msleep(1000);
	}
	
	return a.exec();
}

在上述例子當(dāng)中,我們創(chuàng)建的 QRunnable 類型的指針 BPrint *print 是不需要我們手動(dòng)去回收內(nèi)存的,QThreadPool 在結(jié)束該任務(wù)的執(zhí)行后會(huì)將對(duì)該內(nèi)存進(jìn)行清空

有的小伙伴會(huì)有疑問,既然有 QThread 線程類了,為啥還要用 QRunnable + QThreadPool 創(chuàng)建線程池的方法來使用線程機(jī)制呢,感覺用起來很麻煩啊。所以這里要說明一下此方法的使用場(chǎng)景,當(dāng)線程任務(wù)量非常大的時(shí)候,如果頻繁的創(chuàng)建和釋放 QThread 會(huì)帶來非常大的內(nèi)存開銷,而線程池則可以有效避免這個(gè)問題

還有一個(gè)問題需要注意一下,QThread 是集成自 QObject 的,我們通常會(huì)使用信號(hào)槽與外界進(jìn)行通信。而 QRunnable 并不是繼承自 QObject 類的,所以他無法使用信號(hào)槽機(jī)制進(jìn)行通信。這里推薦兩種方法,一個(gè)是使用 QMetaObject::invokeMethod()函數(shù)。另一個(gè)是使用多重繼承的方法,自定義類需要同時(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;
}

運(yùn)行結(jié)果:
threadfun1 - 1
threadfun2 - 1

threadfun1 - 2
join
detach

根據(jù)輸出結(jié)果可以得知,t1.join() 會(huì)等待t1線程退出后才繼續(xù)往下執(zhí)行,t2.detach() 并不會(huì)等待,detach字符輸出后,主函數(shù)退出,threadfun2還未執(zhí)行完成,但是在主線程退出后,t2的線程也被已經(jīng)被強(qiáng)退出

5. Qt QtConcurrent 之 Run 函數(shù)

Concurrent 是并發(fā)的意思,QtConcurrent 是一個(gè)命名空間,提供了一些高級(jí)的 API,使得所寫的程序可根據(jù)計(jì)算機(jī)的 CPU 核數(shù),自動(dòng)調(diào)整運(yùn)行的線程數(shù)目。這意味著今后編寫的應(yīng)用程序?qū)⒃谖磥聿渴鹪诙嗪讼到y(tǒng)上時(shí)繼續(xù)擴(kuò)展

函數(shù)原型如下:
QFuture<T> QtConcurrent::run(Function function, ...)
QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)

簡(jiǎn)單來說,QtConcurrent::run() 函數(shù)會(huì)在一個(gè)單獨(dú)的線程中執(zhí)行,并且該線程取自全局 QThreadPool,該函數(shù)的返回值通過 QFuture API 提供

需要注意的是:
1)該函數(shù)可能不會(huì)立即運(yùn)行; 函數(shù)只有在線程可用時(shí)才會(huì)運(yùn)行
2)通過 QtConcurrent::run() 返回的 QFuture 不支持取消、暫停,返回的 QFuture 只能用于查詢函數(shù)的運(yùn)行/完成狀態(tài)和返回值
3) Qt Concurrent 已經(jīng)從 QtCore 中移除并成為了一個(gè)獨(dú)立的模塊,所以想要使用 QtConcurrent 需要在 pro 文件中導(dǎo)入模塊:
QT += concurrent

使用方式有以下幾種:
1)將函數(shù)運(yùn)行在某一個(gè)線程中,需要使用 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ù)的計(jì)算結(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";
// 在一個(gè)單獨(dú)的線程中,調(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 = ...;
// 在一個(gè)單獨(dú)的線程中,調(diào)用 QImage 的非常量成員函數(shù) invertPixels(),傳遞給 run() 函數(shù)的參數(shù)是 &image
QFuture<void> future = QtConcurrent::run(&image, &QImage::invertPixels, QImage::InvertRgba);
...
future.waitForFinished();

6)Lambda 表達(dá)式

#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ù)三個(gè)點(diǎn)自適應(yīng)模擬條紋
    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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論