Concurrent Map and Map-Reduce
QtConcurrent::map(), QtConcurrent::mapped() 和 QtConcurrent::mappedReduced() 函数对容器(如 QList 或 QVector)中的每一元素并行计算. QtConcurrent::map() 直接修改容器, QtConcurrent::mapped() 返回新容器, QtConcurrent::mappedReduced() 返回单个结果.
这些函数是 Qt Concurrent 框架的一部分.
上述函数都有一个对应的阻塞函数, 直接返回最终结果, 而不是 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).
Concurrent 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);
map的计算结果可以通过 QFuture获取. 在应用程序中如何使用QFuture详见 QFuture 和 QFutureWatcher.
如果你想修改容器序列, 使用 QtConcurrent::map(). map函数必须如下形式:
U function(T &t);
注意: 上述函数的返回值和返回类型未使用.
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 状态.
Concurrent Map-Reduce
QtConcurrent::mappedReduced() 与 QtConcurrent::mapped()类似, 但是使用新的类型结果代替容器, 结果是使用reduce函数组合成一个简单值.
reduce函数必须如下形式:
V function(T &result, const U &intermediate)
T类型是最终结果, U是函数的返回类型. 注意: reduce函数的返回值和返回类型未使用.
如下方式调用 QtConcurrent::mappedReduced():
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 枚举类型指明调用reduce函数顺序. 如果使用 QtConcurrent::UnorderedReduce (默认), 调用顺序未定义, QtConcurrent::OrderedReduce 表明以容器的元素顺序调用reduce函数.
Additional API Features
使用迭代器代替容器
上述的每个函数都有一个对应的迭代器函数, 使用迭代器范围代替容器. 使用它们的方式与容器参数一样:
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(), and 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, &QImage::rgbSwapped); // create a set of the lengths of all strings in a list QStringList strings = ...; QFuture<QSet<int> > wordLengths = QtConcurrent::mappedReduced(string, &QString::length, &QSet<int>::insert);
注意: 使用 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, QSet<int>::insert);
使用函数对象
QtConcurrent::map(), QtConcurrent::mapped(), QtConcurrent::mappedReduced() 接受函数对象作为映射函数, 这些函数对象用于为函数调用增加状态记录. result_type 必须通过typedef定义为函数的返回类型:
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));
使用多个参数的包装函数
如果你想使用一个包含多个参数的映射函数, 你可以使用lambda函数或std::bind()
将其转换为包含一个参数的函数.
例如, 我们想要使用 QImage::scaledToWidth()作为映射函数:
QImage QImage::scaledToWidth(int width, Qt::TransformationMode) const;
scaledToWidth()函数接受三个参数 (包括 "this" 指针), 不能直接作为QtConcurrent::mapped()的映射函数, 因为 QtConcurrent::mapped() 只接受一个参数的函数. 想要使用 QImage::scaledToWidth() 作为 QtConcurrent::mapped()映射函数, 我们必须为参数 width 和 transformation mode提供默认值:
QList<QImage> images = ...; QFuture<QImage> thumbnails = QtConcurrent::mapped(images, [](const QImage &img) { return img.scaledToWidth(100, Qt::SmoothTransformation); });