Конкуррентная карта и карта-сводка
Функции 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).
Конкуррентная карта
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 за раз, поэтому использование мьютекса для блокировки переменной result не является необходимым. Перечисление 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, &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, &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() принимают объекты функций для функции отображения. Эти объекты функций могут быть использованы для добавления состояния к вызову функции. Тип typedef result_type должен определять тип результата оператора вызова функции:
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); Оборачивание функций, принимающих несколько аргументов
Если вы хотите использовать функцию отображения, которая принимает более одного аргумента, вы можете использовать лямбда-функцию или std::bind() для преобразования ее в функцию, принимающую один аргумент.
В качестве примера мы будем использовать QImage::scaledToWidth():
QImage QImage::scaledToWidth(int width, Qt::TransformationMode) const;
scaledToWidth принимает три аргумента (включая указатель «this») и не может быть использована с QtConcurrent::mapped() напрямую, так как QtConcurrent::mapped() ожидает функцию, принимающую один аргумент. Чтобы использовать QImage::scaledToWidth() с QtConcurrent::mapped(), нам необходимо предоставить значение для ширины и режима преобразования:
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/archives/qt-5.11/qtconcurrentmap.html