并发映射和映射-归约

QtConcurrent::map(), QtConcurrent::mapped() 和 QtConcurrent::mappedReduced() 函数在如QList等序列的项上并行执行计算。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)。

并发映射

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()。然后映射函数必须为以下格式

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 来监控映射的状态。

并发映射-归约

QtConcurrent::mappedReduced() 与 QtConcurrent::mapped() 类似,但不是返回包含新结果的序列,而是使用归约函数将结果组合成一个单独的值。

归约函数的格式必须为

V function(T &result, const U &intermediate)

T 是最终结果类型,U 是映射函数的返回类型。请注意,归约函数的返回值和返回类型不会使用。

这样调用 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枚举提供了控制合并顺序的方法。如果使用QtConcurrent::UnorderedReduce(默认)则顺序未定义,而QtConcurrent::OrderedReduce则确保按照原始序列的顺序进行合并。

附加API功能

使用迭代器而不是序列

上述每个函数都有一个变体,它接受迭代器范围而不是序列。您可以使用它们与序列变体相同的方式使用

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)。

使用成员函数

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()时,可以自由混合使用普通函数、成员函数和lambda表达式。

// 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));

对于reduce函数也支持函数对象

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()期望一个接受单个参数的函数。为了使用QImage::scaledToWidth()与QtConcurrent::mapped(),我们必须提供width转换模式的值。

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

© 2024 Qt公司有限公司。本文档中的文档贡献是各自所有者的版权。本文档是根据自由软件基金会发布的GNU自由文档许可版本1.3的条款许可的。Qt及其相关徽标是芬兰以及/或在全世界其他国家的Qt公司有限公司的商标。所有其他商标均为其各自所有者的财产。