并发 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);