Spec-Zone.ru › Qt

Конкуррентная карта и Map-Reduce

Функции 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> и 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 и QFutureWatcher для получения дополнительной информации о том, как использовать QFuture в ваших приложениях.

Если вы хотите изменить последовательность на месте, используйте 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);

Функция сведения будет вызываться один раз для каждого результата, возвращённого функцией отображения, и должна объединить промежуточный результат в переменную результат. 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> и 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() принимают объекты функций для функции отображения. Эти объекты функций могут быть использованы для добавления состояния к вызову функции:

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<QImage>(images,
                                      Scaled(100),
                                      ImageTransform(),
                                      QtConcurrent::SequentialReduce);

Использование лямбда-выражений

QtConcurrent::map(), QtConcurrent::mapped() и QtConcurrent::mappedReduced() принимают лямбда-выражения для функций отображения и сведения:

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() вы можете свободно смешивать использование обычных функций, методов-членов и лямбда-выражений.

QList<QImage> collage = QtConcurrent::mappedReduced(images,
        [&size](const QImage &image) {
            return image.scaled(size, size);
        },
        addToCollage
   ).results();

Для функции сведения лямбда-выражения напрямую не поддерживаются. Однако лямбда-выражения могут быть использованы, когда тип результата сведения явно указан:

QList<QImage> collage = QtConcurrent::mappedReduced<QImage>(images,
        [&size](const QImage &image) {
            return image.scaled(size, size);
        },
        [](QImage &result, const QImage &value) {
            // do some transformation
        }
   ).results();

Оборачивание функций, принимающих несколько аргументов

Если вы хотите использовать функцию отображения, которая принимает более одного аргумента, вы можете использовать лямбда-функцию или 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 = ...;
std::function<QImage(const QImage &)> scale = [](const QImage &img) {
    return img.scaledToWidth(100, Qt::SmoothTransformation);
};
QFuture<QImage> thumbnails = QtConcurrent::mapped(images, scale);

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.2/qtconcurrentmap.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API