Qt使用QtConcurrent::run實(shí)現(xiàn)異步等待和同步調(diào)用
在使用Qt進(jìn)行開發(fā)時(shí),經(jīng)常需要使用異步方法,不同于C#的async/await,Qt中提供了QtConcurrent::run接口方法可供調(diào)用,習(xí)慣了C#的await,便想著能不能封裝幾個(gè)類似的函數(shù)在項(xiàng)目中使用,探索了下,有如下幾個(gè)方案
首先定義全局線程池:
inline QThreadPool* globalThreadPool()
{
static QThreadPool* pool = []() {
QThreadPool* p = new QThreadPool;
p->setMaxThreadCount(QThread::idealThreadCount());
return p;
}();
return pool;
}
方案一,最簡(jiǎn)單的封裝調(diào)用,直接異步調(diào)用,無(wú)任何返回結(jié)果,也不會(huì)卡住調(diào)用線程:
auto CallAsync = [](auto func){
QtConcurrent::run(globalThreadPool(), func);
};
調(diào)用時(shí)用法如下:
CallAsync([](){
// do something in theadpool...
});
方案二,如果我們想在異步執(zhí)行時(shí),調(diào)用線程同步等待,可封裝如下:
auto AwaitCallAsync = [](auto func, int timeoutSeconds = 5) -> bool
{
QFuture<void> future = QtConcurrent::run(globalThreadPool(), func);
// 使用智能指針管理對(duì)象生命周期
auto watcher = std::make_shared<QFutureWatcher<void>>();
auto loop = std::make_shared<QEventLoop>();
auto timer = std::make_shared<QTimer>();
bool timedOut = false;
if (timeoutSeconds > 0) {
timer->setInterval(timeoutSeconds * 1000);
timer->setSingleShot(true);
QObject::connect(timer.get(), &QTimer::timeout, [&timedOut, loop]() {
timedOut = true;
loop->quit();
});
timer->start();
}
QObject::connect(watcher.get(), &QFutureWatcher<void>::finished, loop.get(), &QEventLoop::quit);
watcher->setFuture(future);
loop->exec();
// 清理資源
if (!timedOut && timeoutSeconds > 0) {
timer->stop();
}
return !timedOut; // 返回是否正常完成
};
此時(shí),執(zhí)行AwaitCallAsync時(shí),調(diào)用線程會(huì)同步等待但并不會(huì)卡住線程,為了避免長(zhǎng)時(shí)間等待,也可以添加超時(shí)參數(shù)。
方案三,有時(shí),我們?cè)谙M诋惒胶瘮?shù)調(diào)用完成后能回到調(diào)用線程繼續(xù)執(zhí)行,那么可以添加QFutureWatcher,監(jiān)控異步函數(shù)的執(zhí)行,然后在QFutureWatcher發(fā)送finished時(shí)執(zhí)行另一個(gè)函數(shù),如下:
auto CallAsyncWithCallback = [](auto func_async, auto func_callback){
auto future = QtConcurrent::run(globalThreadPool(), func_async);
auto watcher = new QFutureWatcher<void>();
// 連接信號(hào),此處connect會(huì)被自動(dòng)執(zhí)行為Qt::QueuedConnection
QObject::connect(watcher, &QFutureWatcher<void>::finished, [func_callback, watcher]() mutable {
func_callback();
watcher->deleteLater(); // 完成后自動(dòng)清理
});
watcher->setFuture(future);
}
上面的connect是在調(diào)用線程中執(zhí)行的,而finished信號(hào)是在線程池中子線程中發(fā)出來(lái)的,跨線程所以Qt會(huì)選擇用Qt::QueuedConnection的方式執(zhí)行Lambda 表達(dá)式。
方案四,有時(shí),我們希望回調(diào)函數(shù)在特定線程比如主線程中執(zhí)行,如下:
auto CallAsyncWithUICallback = [](FuncAsync func_async, FuncCallback func_callback_onUI) {
QtConcurrent::run([func_async, func_callback]() {
func_async(); // 在子線程執(zhí)行異步函數(shù)
// 回到主線程執(zhí)行回調(diào)
QMetaObject::invokeMethod(qApp, [func_callback]() {
func_callback();
}, Qt::QueuedConnection);
});
}
注意,在調(diào)用invokeMethod時(shí),要顯示指定Qt::QueuedConnection。
總體來(lái)說(shuō),C#的async await很靈活很強(qiáng)大,Qt雖然不能與之相比,但經(jīng)過(guò)簡(jiǎn)單的封裝,也能寫出比較靈活或者符合自己業(yè)務(wù)需求而又簡(jiǎn)潔好讀的異步代碼。
到此這篇關(guān)于Qt使用QtConcurrent::run實(shí)現(xiàn)異步等待和同步調(diào)用的文章就介紹到這了,更多相關(guān)Qt QtConcurrent::run異步等待和同步調(diào)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)動(dòng)態(tài)順序表的示例代碼
順序表是用一段物理地址連續(xù)的存儲(chǔ)單元依次存儲(chǔ)數(shù)據(jù)元素的線性結(jié)構(gòu)。順序表一般分為靜態(tài)順序表和動(dòng)態(tài)順序表,本文主要和大家介紹的是動(dòng)態(tài)順序表的實(shí)現(xiàn),需要的可以參考一下2022-10-10
C++程序內(nèi)存棧區(qū)與堆區(qū)模型案例分析
一直以來(lái)總是對(duì)這個(gè)問(wèn)題的認(rèn)識(shí)比較朦朧,我相信很多朋友也是這樣的,總是聽到內(nèi)存一會(huì)在棧上分配,一會(huì)又在堆上分配,那么它們之間到底是怎么的區(qū)別呢,讓我們一起來(lái)看看2022-03-03
C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)與算法之排序總結(jié)(二)
這篇文章住要介紹的是選擇類排序中的簡(jiǎn)單、樹形和堆排序,歸并排序、分配類排序的基數(shù)排序,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2021-12-12
詳解VS2019使用scanf()函數(shù)報(bào)錯(cuò)的解決方法
本文主要介紹了詳解VS2019使用scanf()函數(shù)報(bào)錯(cuò)的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
C語(yǔ)言詳解如何實(shí)現(xiàn)帶頭雙向循環(huán)鏈表
帶頭雙向循環(huán)鏈表:結(jié)構(gòu)最復(fù)雜,一般用在單獨(dú)存儲(chǔ)數(shù)據(jù)。實(shí)際中使用的鏈表數(shù)據(jù)結(jié)構(gòu),都是帶頭雙向循環(huán)鏈表。另外這個(gè)結(jié)構(gòu)雖然結(jié)構(gòu)復(fù)雜,但是使用代碼實(shí)現(xiàn)以后會(huì)發(fā)現(xiàn)結(jié)構(gòu)會(huì)帶來(lái)很多優(yōu)勢(shì),實(shí)現(xiàn)反而簡(jiǎn)單2022-04-04

