簡述
QFuture 類代表一個異步計算的結果。
要啟動一個計算,使用 Qt之Concurrent框架 中的 APIs 之一。
QFuture 允許線程與一個或多個結果同步,這些結果將在稍后的時間點准備就緒,該結果可以是具有默認構造函數和拷貝構造函數的任何類型。如果一個結果在調用 result()、resultAt() 或 results() 函數時不可用,QFuture 將進行等待,直到結果可用為止,使用 isResultReadyAt() 函數可以檢測結果是否已准備就緒。
進度信息由 progressValue()、progressMinimum()、progressMaximum() 和 progressText() 函數提供。
基本使用
要在另一個線程中運行函數,使用 QtConcurrent::run():
1 #include <QCoreApplication>
2 #include <QtConcurrent/QtConcurrentRun>
3 #include <QDebug>
4
5 void hello(const QString &name) 6 { 7 qDebug() << "Hello" << name << "from" << QThread::currentThread(); 8 } 9
10 int main(int argc, char *argv[]) 11 { 12 QCoreApplication a(argc, argv); 13
14 qDebug() << "Main Thread" << QThread::currentThread(); 15
16 // 在一個單獨的線程中調用 hello()
17 QFuture<void> f1 = QtConcurrent::run(hello, QString("Qter")); 18 QFuture<void> f2 = QtConcurrent::run(hello, QString("Pythoner")); 19
20 // 阻塞調用線程並等待計算完成,確保所有結果可用
21 f1.waitForFinished(); 22 f2.waitForFinished(); 23 }
這時,輸出如下:
Main Thread QThread(0x398fc0)
Hello “Qter” from QThread(0x39c240, name = “Thread (pooled)”)
Hello “Pythoner” from QThread(0x39c280, name = “Thread (pooled)”)
顯然,主線程與另外兩個線程均不同。
QFuture <void>
專用於獲取不包含任何結果的函數,任何 QFuture <T>
都可以分配或復制到 QFuture <void>
。如果只需要狀態或進度信息,而不需要實際結果數據,這是非常有用的。
高級操作
QFuture 還提供了很多方法與正在進行的異步調用進行交互。例如,使用 cancel() 函數取消計算;要暫停計算,使用 setPaused() 函數或 pause(),resume()、togglePaused() 便利函數之一。
助手中有很關鍵的一句話:
Be aware that not all asynchronous computations can be canceled or paused. For example, the future returned by QtConcurrent::run() cannot be canceled; but the future returned by QtConcurrent::mappedReduced() can
意思是說,並非所有異步計算都可以取消或暫停。例如,QtConcurrent::run() 返回的 future 不能被取消,但 QtConcurrent::mappedReduced() 返回的可以。
來看一個對一組圖像進行縮放的示例:
1 #include <QImage>
2 #include <QDebug>
3 #include <QGuiApplication>
4 #include <QtConcurrent/QtConcurrentMap>
5
6 QImage scale(const QImage &image) 7 { 8 qDebug() << "Scaling image in thread" << QThread::currentThread(); 9 return image.scaled(QSize(100, 100), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 10 } 11
12 int main(int argc, char *argv[]) 13 { 14 QGuiApplication app(argc, argv); 15
16 const int imageCount = 20; 17
18 // 創建一個列表包含 imageCount 個圖像
19 QList<QImage> images; 20 for (int i = 0; i < imageCount; ++i) 21 images.append(QImage(1600, 1200, QImage::Format_ARGB32_Premultiplied)); 22
23 // 使用 QtConcurrent::mapped 對每個圖像應用 scale 函數(縮放)
24 QFuture<QImage> thumbnails = QtConcurrent::mapped(images, scale); 25
26 // 暫停計算
27 thumbnails.pause(); 28 qDebug() << "isPaused" << thumbnails.isPaused(); 29
30 // 恢復計算
31 if(thumbnails.isPaused()) { 32 thumbnails.resume(); 33 qDebug() << "isStarted" << thumbnails.isPaused(); 34 } 35
36 // 阻塞調用線程並等待計算完成,確保所有結果可用
37 thumbnails.waitForFinished(); 38
39 // 返回 future 的所有結果
40 QList<QImage> list = thumbnails.results(); 41 foreach (QImage image, list) { 42 qDebug() << image; 43 } 44
45 qDebug() << "********************"; 46
47 // 返回 future 的結果數
48 int nCount = thumbnails.resultCount(); 49 for (int i = 0; i < nCount; ++i) { 50 QImage image = thumbnails.resultAt(i); 51 qDebug() << image; 52 } 53
54 return 0; 55 }
為了演示效果,調用 mapped() 之后,使用 pause() 暫停計算,然后通過 isPaused() 來查詢 QFuture 表示的計算狀態。狀態可以使用 isCanceled()、isStarted()、isFinished()、isRunning() 或 isPaused() 函數來查詢。
注意:即使 isPaused() 返回 true,計算可能仍在運行。
要訪問 future 中結果,可以使用索引位置。一些 QFuture 的成員函數將索引作為它們的第一個參數(例如:resultAt()),使得可以在不使用迭代器的情況下訪問。
對於擁有多個結果的 QFuture 對象,resultCount() 函數返回連續結果的數量。這意味着,從 0 到 resultCount() 的結果始終是安全的。
迭代器
QFuture 提供了一個 Java 風格迭代器(QFutureIterator)和一個 STL 風格迭代器(QFuture::const_iterator):
- Java 風格迭代器:高級、易於使用;但另一方面,效率略低。
- STL 風格迭代器:低級、使用麻煩;但另一方面,稍微更快。對於了解 STL 的人來說,可以很快上手。
使用這些迭代器是訪問 future 中的結果的另一種方法。
QFutureIterator
QFutureIterator 類為 QFuture 提供了一個 Java 風格的 const 迭代器。
QFutureIterator <T>
允許遍歷 QFuture <T>
。 注意:對於 QFuture,沒有可變迭代器(不像其他的 Java 風格迭代器)。
QFutureIterator 構造函數使用 QFuture 作為其參數。在構造之后,迭代器位於結果列表的最開始(即:在第一個結果之前)。 以下是按順序遍歷所有結果的方法:
1 QFuture<QString> future; 2 ... 3 QFutureIterator<QString> i(future); 4 while (i.hasNext()) 5 qDebug() << i.next();
next() 函數從 future 返回下一個結果(如果需要,等待它變得可用) 並推進迭代器。與 STL 風格迭代器不同,Java 風格迭代器指向結果之間,而不是直接指向結果。第一次調用 next() 將迭代器推進到第一個和第二個結果之間的位置,並返回第一個結果;第二次調用 next() 將迭代器推進到第二和第三個結果之間的位置,並返回第二個結果;以此類推。
下面是如何在相反的順序遍歷的元素:
1 QFutureIterator<QString> i(future); 2 i.toBack(); 3 while (i.hasPrevious()) 4 qDebug() << i.previous();
如果要查找特定值的所有出現,可以在循環中使用 findNext() 或 findPrevious()。
可以在同一個 future 中使用多個迭代器,如果 future 在 QFutureIterator 處於活動狀態時被修改,QFutureIterator 將繼續迭代原始的 future,忽略修改的副本。
QFuture::const_iterator
QFuture :: const_iterator 類為 QFuture 提供了一個 STL 風格的 const 迭代器。
默認的 QFuture::const_iterator 構造函數會創建一個未初始化的迭代器。在開始迭代之前,必須使用 QFuture::constBegin() 或 QFuture::constEnd() 等 QFuture 函數進行初始化。
這是一個打印所有 QFuture 中可用的結果的典型循環:
1 QFuture<QString> future = ...; 2
3 QFuture<QString>::const_iterator i; 4 for (i = future.constBegin(); i != future.constEnd(); ++i) 5 cout << *i << endl;