并發 Map 和 Map-Reduce
- QtConcurrent::map()會對容器中的每個項目應用一個函數,對項目進行就地修改。
- QtConcurrent::mapped() 類似于 map(),但它返回的是一個包含修改內容的新容器。
- QtConcurrent::mappedReduced() 類似于 mapped(),只不過修改后的結果被縮小或折疊成一個結果。
上述每個函數都有一個阻塞變體,它返回最終結果而不是QFuture?。使用它們的方法與異步變體相同。
QList<QImage> images = ...;// Each call blocks until the entire operation is finished.
QList<QImage> future = QtConcurrent::blockingMapped(images, scaled);QtConcurrent::blockingMap(images, scale);QImage collage = QtConcurrent::blockingMappedReduced(images, scaled, addToCollage);
請注意,上述結果類型不是QFuture?對象,而是真正的結果類型(本例中為QList<QImage> 和 QImage)。
并發 Map
QtConcurrent::mapped() 接收一個輸入序列(=容器)和一個映射函數。然后,序列中的每個項目都會調用該映射函數,并返回一個包含映射函數返回值的新序列。
映射函數的形式必須是
U function(const T &t);
T 和 U 可以是任何類型(甚至可以是相同類型),但 T 必須與序列(序列=容器)中存儲的類型相匹配。函數返回修改或映射后的內容。
本例展示了如何對序列中的所有項目應用縮放函數:
QImage scaled(const QImage &image)
{return image.scaled(100, 100);
}QList<QImage> images = ...;
QFuture<QImage> thumbnails = QtConcurrent::mapped(images, scaled);
映射結果可通過QFuture?獲取。有關如何在應用程序中使用QFuture?的詳細信息,請參閱QFuture?和QFutureWatcher?文檔。
如果您想就地修改序列,請使用 QtConcurrent::map()。map 函數的形式必須是
U function(T &t);
請注意,map 函數的返回值和返回類型不會被使用。
使用 QtConcurrent::map() 類似于使用 QtConcurrent::mapped():
void scale(QImage &image)
{image = image.scaled(100, 100);
}QList<QImage> images = ...;
QFuture<void> future = QtConcurrent::map(images, scale);
由于序列是就地修改的,QtConcurrent::map() 不會通過QFuture?返回任何結果。不過,您仍然可以使用QFuture?和QFutureWatcher?來監控映射的狀態。
并發 Map-Reduce
QtConcurrent::mappedReduced() 類似于 QtConcurrent::mapped(),但不是返回包含新結果的序列,而是使用 reduce 函數將結果合并為一個值。
reduce 函數的形式必須是
V function(T &result, const U &intermediate)
T 是最終結果的類型,U 是映射函數的返回類型。注意,不使用 reduce 函數的返回值和返回類型。
像這樣調用 QtConcurrent::mappedReduced():
QImage scaled(const QImage &image)
{return image.scaled(100, 100);
}void addToCollage(QImage &collage, const QImage &thumbnail)
{QPainter p(&collage);static QPoint offset = QPoint(0, 0);p.drawImage(offset, thumbnail);offset += ...;
}QList<QImage> images = ...;
QFuture<QImage> collage = QtConcurrent::mappedReduced(images, scaled, addToCollage);
map 函數返回的每個結果都將調用一次 reduce 函數,并將中間?結果合并到結果變量中。QtConcurrent::mappedReduced() 保證每次只有一個線程調用 reduce,因此無需使用互斥來鎖定結果變量。QtConcurrent::ReduceOptions?枚舉提供了一種控制還原順序的方法。如果使用QtConcurrent::UnorderedReduce?(默認值),則順序未定義,而QtConcurrent::OrderedReduce?可確保按照原始序列的順序進行還原。
其他應用程序接口功能
使用迭代器代替序列
上述每個函數都有一個使用迭代器范圍而非序列的變體。使用方法與序列變體相同:
QList<QImage> images = ...;QFuture<QImage> thumbnails = QtConcurrent::mapped(images.constBegin(), images.constEnd(), scaled);// Map in-place only works on non-const iterators.
QFuture<void> future = QtConcurrent::map(images.begin(), images.end(), scale);QFuture<QImage> collage = QtConcurrent::mappedReduced(images.constBegin(), images.constEnd(), scaled, addToCollage);
阻塞變體
上述每個函數都有一個阻塞變體,它返回最終結果而不是QFuture?。使用方法與異步變體相同。
QList<QImage> images = ...;// Each call blocks until the entire operation is finished.
QList<QImage> future = QtConcurrent::blockingMapped(images, scaled);QtConcurrent::blockingMap(images, scale);QImage collage = QtConcurrent::blockingMappedReduced(images, scaled, addToCollage);
請注意,上述結果類型不是QFuture?對象,而是真正的結果類型(本例中為QList<QImage> 和 QImage)。
使用成員函數
QtConcurrent::map()、QtConcurrent::mapped()和 QtConcurrent::mappedReduced() 接受指向成員函數的指針。成員函數類的類型必須與存儲在序列中的類型相匹配:
// Squeeze all strings in a QStringList.
QStringList strings = ...;
QFuture<void> squeezedStrings = QtConcurrent::map(strings, &QString::squeeze);// Swap the rgb values of all pixels on a list of images.
QList<QImage> images = ...;
QFuture<QImage> bgrImages = QtConcurrent::mapped(images,static_cast<QImage (QImage::*)() const &>(&QImage::rgbSwapped));// Create a set of the lengths of all strings in a list.
QStringList strings = ...;
QFuture<QSet<int>> wordLengths = QtConcurrent::mappedReduced(strings, &QString::length,qOverload<const int&>(&QSet<int>::insert));
注意qOverload?的使用。需要使用它來解決有多個重載的方法的歧義問題。
還請注意,在使用 QtConcurrent::mappedReduced() 時,您可以自由混合使用普通函數和成員函數:
// Can mix normal functions and member functions with QtConcurrent::mappedReduced().// Compute the average length of a list of strings.
extern void computeAverage(int &average, int length);
QStringList strings = ...;
QFuture<int> averageWordLength = QtConcurrent::mappedReduced(strings, &QString::length, computeAverage);// Create a set of the color distribution of all images in a list.
extern int colorDistribution(const QImage &string);
QList<QImage> images = ...;
QFuture<QSet<int>> totalColorDistribution = QtConcurrent::mappedReduced(images, colorDistribution,qOverload<const int&>(&QSet<int>::insert));
使用函數對象
QtConcurrent::map()、QtConcurrent::mapped()和QtConcurrent::mappedReduced()接受map函數的函數對象。這些函數對象可用于為函數調用添加狀態:
struct Scaled
{Scaled(int size): m_size(size) { }typedef QImage result_type;QImage operator()(const QImage &image){return image.scaled(m_size, m_size);}int m_size;
};QList<QImage> images = ...;
QFuture<QImage> thumbnails = QtConcurrent::mapped(images, Scaled(100));
還原函數也支持函數對象:
struct ImageTransform
{void operator()(QImage &result, const QImage &value);
};QFuture<QImage> thumbNails =QtConcurrent::mappedReduced(images, Scaled(100), ImageTransform(),QtConcurrent::SequentialReduce);
使用 Lambda 表達式
QtConcurrent::map()、QtConcurrent::mapped()和 QtConcurrent::mappedReduced() 接受 map 和 reduce 函數的 lambda 表達式:
QList<int> vector { 1, 2, 3, 4 };
QtConcurrent::blockingMap(vector, [](int &x) { x *= 2; });int size = 100;
QList<QImage> images = ...;QList<QImage> thumbnails = QtConcurrent::mapped(images,[&size](const QImage &image) {return image.scaled(size, size);}).results();
當使用 QtConcurrent::mappedReduced() 或 QtConcurrent::blockingMappedReduced() 時,您可以自由混合使用普通函數、成員函數和 lambda 表達式
QList<QImage> collage = QtConcurrent::mappedReduced(images,[&size](const QImage &image) {return image.scaled(size, size);},addToCollage).results();
您還可以將 lambda 傳遞給 reduce 對象:
QList<QImage> collage = QtConcurrent::mappedReduced(images,[&size](const QImage &image) {return image.scaled(size, size);},[](QImage &result, const QImage &value) {// do some transformation}).results();
封裝包含多個參數的函數
如果要使用一個包含多個參數的 map 函數,可以使用 lambda 函數或std::bind()
?將其轉換為一個包含一個參數的函數。
例如,我們將使用 QImage::scaledToWidth():
QImage QImage::scaledToWidth(int width, Qt::TransformationMode) const;
scaledToWidth 需要三個參數(包括 "this "指針),并且不能直接與 QtConcurrent::mapped() 一起使用,因為 QtConcurrent::mapped() 期望使用一個參數的函數。要在 QtConcurrent::mapped() 中使用 QImage::scaledToWidth(),我們必須提供寬度值和轉換模式:
QList<QImage> images = ...;
std::function<QImage(const QImage &)> scale = [](const QImage &img) {return img.scaledToWidth(100, Qt::SmoothTransformation);
};
QFuture<QImage> thumbnails = QtConcurrent::mapped(images, scale);
Concurrent Map and Map-Reduce | Qt Concurrent 6.8.2