Qt多線程:QtConcurrent + QFuture + QFutureWatcher

QtConcurrent實際是一個命名空間,該命名空間提供了高級API,從而可以在不使用低級線程原語(啟動線程、線程間同步、鎖等)的情況下編寫多線程程序。

但是QtConcurrent僅支持接受純函數(shù)或者lambda表達式,不支持信號和槽,如果需要監(jiān)聽任務(wù)執(zhí)行結(jié)果可以通過與QFuture和QFutureWatcher配合來達到。

QFuture類表示異步計算的結(jié)果,使用Qt Concurrent框架中的API啟用。

QFuture允許線程針對一個或多個結(jié)果進行同步,這些結(jié)果將在稍后的時間點準(zhǔn)備就緒。結(jié)果可以是具有默認構(gòu)造函數(shù)和副本構(gòu)造函數(shù)的任何類型。如果在調(diào)用result(),resultAt()或results()函數(shù)時結(jié)果不可用,則QFuture將等待,直到結(jié)果變?yōu)榭捎?。您可以使用isResultReadyAt()函數(shù)來確定結(jié)果是否準(zhǔn)備就緒。對于報告多個結(jié)果的QFuture對象,resultCount()函數(shù)返回連續(xù)結(jié)果的數(shù)量。這意味著將結(jié)果從0迭代到resultCount()始終是安全的。
QFuture提供了Java樣式的迭代器(QFutureIterator)和STL樣式的迭代器(QFuture :: const_iterator)。使用這些迭代器是將來獲取結(jié)果的另一種方法。
QFuture還提供了與運行計算進行交互的方法。例如,可以使用cancel()函數(shù)取消計算。要暫停計算,請使用setPaused()函數(shù)或pause(),resume()或togglePaused()便利函數(shù)之一。請注意,并非所有異步計算都可以取消或暫停。例如,不能取消QtConcurrent :: run()返回的future。但是QtConcurrent :: mappedReduced()返回的Future可以。
進度信息由progressValue(),progressMinimum(),progressMaximum()和progressText()函數(shù)提供。 waitForFinished()函數(shù)使調(diào)用線程阻塞并等待計算完成,確保所有結(jié)果均可用。
可以使用isCanceled(),isStarted(),isFinished(),isRunning()或isPaused()函數(shù)查詢QFuture表示的計算狀態(tài)。
QFuture是輕量級引用計數(shù)類,可以按值傳遞。
QFuture <void>專門用于不包含任何結(jié)果獲取功能。任何QFuture <T>都可以分配或復(fù)制到QFuture <void>中。如果僅需要狀態(tài)或進度信息,而無需實際結(jié)果數(shù)據(jù),則此功能很有用。

要使用信號和插槽與正在運行的任務(wù)進行交互,則需要使用QFutureWatcher。

QFutureWatcher類允許使用信號和插槽監(jiān)視QFuture。

QFutureWatcher提供有關(guān)QFuture的信息和通知。使用setFuture()函數(shù)開始監(jiān)視特定的QFuture。 future()函數(shù)返回帶有setFuture()的Future集。
為了方便起見,QFutureWatcher中還提供了一些函數(shù):progressValue(),progressMinimum(),progressMaximum(),progressText(),isStarted(),isFinished(),isRunning(),isCanceled(),isPaused(),waitForFinished (),result()和resultAt()。 以及一些槽函數(shù)cancel(),setPaused(),pause(),resume()和togglePaused()。
狀態(tài)更改通過started(),finished(),canceled(),paused(),resumed(),resultReadyAt()和resultReadyAt()信號進行報告。從progressRangeChanged(),void progressValueChanged()和progressTextChanged()信號提供進度信息。
節(jié)流控制由setPendingResultsLimit()函數(shù)提供。當(dāng)待處理的resultReadyAt()或resultsReadyAt()信號的數(shù)量超過限制時,由Future表示的計算將自動受到限制。一旦待處理信號的數(shù)量下降到限制以下,計算將恢復(fù)。

簡單的例子:

#include "mainwindow.h"

#include "ui_mainwindow.h"

#include <QtConcurrent>

#include <QThread>

#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :

    QMainWindow(parent),

    ui(new Ui::MainWindow)

{

    ui->setupUi(this);

    m_pWatcher = new(std::nothrow) QFutureWatcher<int>(this);

    connect(m_pWatcher, &QFutureWatcher<int>::progressValueChanged, [](int nVal){

        qDebug() << "watcher => " << QThread::currentThreadId() << QThread::currentThread() << " " << nVal << endl;

    });

    connect(m_pWatcher, &QFutureWatcher<int>::resultReadyAt, [this](int index){

        qDebug() << "Result At " << index << " is " << m_pWatcher->resultAt(index) << endl;

    });

}

MainWindow::~MainWindow()

{

    delete ui;

}

int func(int nVal)

{

    qDebug() << "start => " << QThread::currentThreadId() << QThread::currentThread() << " " << nVal << endl;

    return nVal *2;

}

void MainWindow::on_btnStart_clicked()

{

    QList<int> lst;

    lst.push_back(1);

    lst.push_back(2);

    lst.push_back(3);

    lst.push_back(4);

    lst.push_back(5);

    lst.push_back(6);

    m_pWatcher->setFuture(QtConcurrent::mapped(lst, func));

}

![運行結(jié)果]
image.png

通過運行結(jié)果可以發(fā)現(xiàn),QtConccurent管理的線程實際是從線程池分配線程資源的,而綁定QFutureWatcher的槽是在主線程中執(zhí)行的。
在需要單次執(zhí)行且內(nèi)部邏輯較簡單的時候使用QtConccurrent+QFuture+QFutureWatcher是很方便的,可以減少很多編碼工作量,而且在多cpu環(huán)境中,QtConccurent也會啟用多核。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容