Qt中的 Concurrent 模塊為我們提供了在一個序列容器類型針對每一個元素的多線程並行運算,比如 QList 、 QVector 。
- QtConcurrent::map() :對序列的每一項元素都應用一個函數,並將運算結果替換原來的元素。
- QtConcurrent::mapped() :功能類似 map() 函數,它會返回一個新容器存儲函數處理后的結果。
- QtConcurrent::mappedReduced() :類似於 mapped() ,他會將這個返回的結果序列,經過另一個函數處理為一個單個的值。
1. map 和 mapped
QtConcurrent::map 和 QtConcurrent::mapped 的基本形式如下:
QFuture<void> map(Sequence &sequence, MapFunctor map)
- 參數 sequence :表示序列容器(如QList、QVector等)
- 參數 map :表示函數。函數的形式必須符合 U function(T &t); 形式。
類型 U 和 T 可以為任何類型,但是 T 的類型必須與容器中的類型保持一致。返回值 U QtConcurrent::map 函數中並沒有用到。 - 返回值 QFuture<void> 。關於 QFuture 這里就不過多介紹了,了解更多可以參考上一篇文章 QtConcurrent多線程 - run()與QFuture
下面是一個簡單的使用示例:
1 void scale(QImage &image) 2 { 3 image = image.scaled(100, 100); 4 } 5
6 QList<QImage> images = ...; 7 QFuture<void> future = QtConcurrent::map(images, scale);
該示例中,多線程對 QList<QImage> 容器中的每一個 QImage 縮放到 100 * 100 的尺寸。結果會覆蓋掉原容器中的元素。
QtConcurrent::mapped 函數跟 QtConcurrent::map 函數類似,不同之處在於:
- 傳遞的函數形式不同。它的形式為, U function(const T &t); ,一個常引用做為參數,表示容器中的元素不可修改。返回值 U 存入整個 QtConcurrent::mapped 計算后的新容器中返回。
- 函數 QtConcurrent::mapped 的返回值為 QFuture<U> ,QFuture 除了可以表示單個類型也可以一組序列容器類型,跟單個的類似,可以使用 results 函數返回運算后的結果。
下面是使用 QtConcurrent::mapped 的例子
1 QImage scaled(const QImage &image) 2 { 3 return image.scaled(100, 100); 4 } 5
6 QList<QImage> images = ...; 7 QFuture<QImage> thumbnails = QtConcurrent::mapped(images, scaled);
這里同樣也是對列表中的 QImage 進行縮放到 100 * 100 大小,但是容器本身的內容沒有改變。
使用如下代碼獲取運算結果:
1 funcFunture2.waitForFinished(); // 等待運算處理完成
2 thumbnails.results(); // 獲取運算結果
2. mappedReduced
mappedReduced() 類似於 QtConcurrent::mapped() ,但是它不是返回一個帶有新結果的序列,而是使用reduce函數將結果組合成一個單獨的值。
它的函數基本形式如下:
1 template <typename Sequence, typename MapFunctor, typename ReduceFunctor>
2 QFuture<typename QtPrivate::ReduceResultType<ReduceFunctor>::ResultType>
3 mappedReduced(const Sequence &sequence, 4 MapFunctor map, 5 ReduceFunctor reduce, 6 ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce))
- 參數 sequence :表示序列容器。
- 參數 map :同 mapped 函數,形式為 U function(const T &t); 的函數。
- 參數 reduce :處理返回后的結果序列的函數。形式為 V function(T &result, const U &intermediate) , T 為最終結果類型, U 為 map 函數的返回類型,V mappedReduced 並沒有使用此返回值。
- 參數 options :reduce 函數的執行順序,默認為任意順序。設置為 OrderedReduce 表示按照原始序列的順序執行。而 SequentialReduce 則表示同一時刻,只有一個線程在執行 reduce 函數,設置為此標志時,可以不用對 reduce 函數進行加鎖操作。
- 返回值: 返回 QFuture<T> ,這個 T 類型就是 reduce 中的第一個參數。
下面是一個示例:
1 void addToCollage(QImage &collage, const QImage &thumbnail) 2 { 3 QPainter p(&collage); 4 static QPoint offset = QPoint(0, 0); 5 p.drawImage(offset, thumbnail); 6 offset += ...; 7 } 8
9 QList<QImage> images = ...; 10 QFuture<QImage> collage = QtConcurrent::mappedReduced(images, scaled, addToCollage);
這里 images 中的每一個元素執行完 scaled 函數后的序列,序列中的每一個元素再執行函數 addToCollage ,最后將計算結構返回。
3. 其他擴展
使用迭代器作為序列容器的輸入范圍。
1 QList<QImage> images = ...; 2
3 QFuture<QImage> thumbnails = QtConcurrent::mapped(images.constBegin(), images.constEnd(), scaled); 4
5 // map in-place only works on non-const iterators
6 QFuture<void> future = QtConcurrent::map(images.begin(), images.end(), scale); 7
8 QFuture<QImage> collage = QtConcurrent::mappedReduced(images.constBegin(), images.constEnd(), scaled, addToCollage);
阻塞等待形式
map 、mapped和mappedReduced為非阻塞形式的多線程執行,可以使用 QFuture 和 QFutureWatcher 作異步通知。但是Qt同樣也提供了阻塞版本的函數:
1 QList<QImage> images = ...; 2
3 // each call blocks until the entire operation is finished
4 QList<QImage> future = QtConcurrent::blockingMapped(images, scaled); 5
6 QtConcurrent::blockingMap(images, scale); 7
8 QImage collage = QtConcurrent::blockingMappedReduced(images, scaled, addToCollage);
這里需要注意的是,他們的返回值不是 QFuture ,而是真實的結果類型。(在這個示例中,返回 QImage 和 QList<QImage>)
使用類的成員函數
1 // squeeze all strings in a QStringList
2 QStringList strings = ...; 3 QFuture<void> squeezedStrings = QtConcurrent::map(strings, &QString::squeeze); 4
5 // swap the rgb values of all pixels on a list of images
6 QList<QImage> images = ...; 7 QFuture<QImage> bgrImages = QtConcurrent::mapped(images, &QImage::rgbSwapped); 8
9 // create a set of the lengths of all strings in a list
10 QStringList strings = ...; 11 QFuture<QSet<int> > wordLengths = QtConcurrent::mappedReduced(string, &QString::length, &QSet<int>::insert);
仿函數
1 struct Scaled 2 { 3 Scaled(int size) : m_size(size) { } 4 typedef QImage result_type; 5
6 QImage operator()(const QImage &image) 7 { 8 return image.scaled(m_size, m_size); 9 } 10
11 int m_size; 12 }; 13
14 QList<QImage> images = ...; 15 QFuture<QImage> thumbnails = QtConcurrent::mapped(images, Scaled(100));
對於 reduce 函數,不直接支持函數對象。但是,當顯式指定約簡結果的類型時,可以使用仿函數:
1 struct ImageTransform 2 { 3 void operator()(QImage &result, const QImage &value); 4 }; 5
6 QFuture<QImage> thumbNails =
7 QtConcurrent::mappedReduced<QImage>(images, 8 Scaled(100), 9 ImageTransform(), 10 QtConcurrent::SequentialReduce);
lambda或使用std::bind,處理多參數函數
比如這個函數
QImage QImage::scaledToWidth(int width, Qt::TransformationMode) const;
可以借助 lambda 表達式處理參數
1 QList<QImage> images = ...; 2 std::function<QImage(const QImage &)> scale = [](const QImage &img) { 3 return img.scaledToWidth(100, Qt::SmoothTransformation); 4 }; 5 QFuture<QImage> thumbnails = QtConcurrent::mapped(images, scale);