Concurrent Map and Map-Reduce▲
The QtConcurrent::map(), QtConcurrent::mapped() and QtConcurrent::mappedReduced() functions run computations in parallel on the items in a sequence such as a QList. QtConcurrent::map() modifies a sequence in-place, QtConcurrent::mapped() returns a new sequence containing the modified content, and QtConcurrent::mappedReduced() returns a single result.
These functions are part of the Qt Concurrent framework.
Each of the above functions has a blocking variant that returns the final result instead of a QFuture. You use them in the same way as the asynchronous variants.
QList&
lt;QImage&
gt; images =
...;
// Each call blocks until the entire operation is finished.
QList&
lt;QImage&
gt; future =
QtConcurrent::
blockingMapped(images, scaled);
QtConcurrent::
blockingMap(images, scale);
QImage collage =
QtConcurrent::
blockingMappedReduced(images, scaled, addToCollage);
Note that the result types above are not QFuture objects, but real result types (in this case, QList<QImage> and QImage).
Concurrent Map▲
QtConcurrent::mapped() takes an input sequence and a map function. This map function is then called for each item in the sequence, and a new sequence containing the return values from the map function is returned.
The map function must be of the form:
U function(const
T &
amp;t);
T and U can be any type (and they can even be the same type), but T must match the type stored in the sequence. The function returns the modified or mapped content.
This example shows how to apply a scale function to all the items in a sequence:
QImage scaled(const
QImage &
amp;image)
{
return
image.scaled(100
, 100
);
}
QList&
lt;QImage&
gt; images =
...;
QFuture&
lt;QImage&
gt; thumbnails =
QtConcurrent::
mapped(images, scaled);
The results of the map are made available through QFuture. See the QFuture and QFutureWatcher documentation for more information on how to use QFuture in your applications.
If you want to modify a sequence in-place, use QtConcurrent::map(). The map function must then be of the form:
U function(T &
amp;t);
Note that the return value and return type of the map function are not used.
Using QtConcurrent::map() is similar to using QtConcurrent::mapped():
void
scale(QImage &
amp;image)
{
image =
image.scaled(100
, 100
);
}
QList&
lt;QImage&
gt; images =
...;
QFuture&
lt;void
&
gt; future =
QtConcurrent::
map(images, scale);
Since the sequence is modified in place, QtConcurrent::map() does not return any results via QFuture. However, you can still use QFuture and QFutureWatcher to monitor the status of the map.
Concurrent Map-Reduce▲
QtConcurrent::mappedReduced() is similar to QtConcurrent::mapped(), but instead of returning a sequence with the new results, the results are combined into a single value using a reduce function.
The reduce function must be of the form:
V function(T &
amp;result, const
U &
amp;intermediate)
T is the type of the final result, U is the return type of the map function. Note that the return value and return type of the reduce function are not used.
Call QtConcurrent::mappedReduced() like this:
void
addToCollage(QImage &
amp;collage, const
QImage &
amp;thumbnail)
{
QPainter p(&
amp;collage);
static
QPoint offset =
QPoint(0
, 0
);
p.drawImage(offset, thumbnail);
offset +=
...;
}
QList&
lt;QImage&
gt; images =
...;
QFuture&
lt;QImage&
gt; collage =
QtConcurrent::
mappedReduced(images, scaled, addToCollage);
The reduce function will be called once for each result returned by the map function, and should merge the intermediate into the result variable. QtConcurrent::mappedReduced() guarantees that only one thread will call reduce at a time, so using a mutex to lock the result variable is not necessary. The QtConcurrent::ReduceOptions enum provides a way to control the order in which the reduction is done. If QtConcurrent::UnorderedReduce is used (the default), the order is undefined, while QtConcurrent::OrderedReduce ensures that the reduction is done in the order of the original sequence.
Additional API Features▲
Using Iterators instead of Sequence▲
Each of the above functions has a variant that takes an iterator range instead of a sequence. You use them in the same way as the sequence variants:
QList&
lt;QImage&
gt; images =
...;
QFuture&
lt;QImage&
gt; thumbnails =
QtConcurrent::
mapped(images.constBegin(), images.constEnd(), scaled);
// Map in-place only works on non-const iterators.
QFuture&
lt;void
&
gt; future =
QtConcurrent::
map(images.begin(), images.end(), scale);
QFuture&
lt;QImage&
gt; collage =
QtConcurrent::
mappedReduced(images.constBegin(), images.constEnd(), scaled, addToCollage);
Blocking Variants▲
Each of the above functions has a blocking variant that returns the final result instead of a QFuture. You use them in the same way as the asynchronous variants.
QList&
lt;QImage&
gt; images =
...;
// Each call blocks until the entire operation is finished.
QList&
lt;QImage&
gt; future =
QtConcurrent::
blockingMapped(images, scaled);
QtConcurrent::
blockingMap(images, scale);
QImage collage =
QtConcurrent::
blockingMappedReduced(images, scaled, addToCollage);
Note that the result types above are not QFuture objects, but real result types (in this case, QList<QImage> and QImage).
Using Member Functions▲
QtConcurrent::map(), QtConcurrent::mapped(), and QtConcurrent::mappedReduced() accept pointers to member functions. The member function class type must match the type stored in the sequence:
// Squeeze all strings in a QStringList.
QStringList strings =
...;
QFuture&
lt;void
&
gt; squeezedStrings =
QtConcurrent::
map(strings, &
amp;QString::
squeeze);
// Swap the rgb values of all pixels on a list of images.
QList&
lt;QImage&
gt; images =
...;
QFuture&
lt;QImage&
gt; bgrImages =
QtConcurrent::
mapped(images,
static_cast
&
lt;QImage (QImage::
*
)() const
&
amp;&
gt;(&
amp;QImage::
rgbSwapped));
// Create a set of the lengths of all strings in a list.
QStringList strings =
...;
QFuture&
lt;QSet&
lt;int
&
gt;&
gt; wordLengths =
QtConcurrent::
mappedReduced(strings, &
amp;QString::
length,
qOverload&
lt;const
int
&
amp;&
gt;(&
amp;QSet&
lt;int
&
gt;::
insert));
Note the use of qOverload. It is needed to resolve the ambiguity for the methods, that have multiple overloads.
Also note that when using QtConcurrent::mappedReduced(), you can mix the use of normal and member functions freely:
// Can mix normal functions and member functions with QtConcurrent::mappedReduced().
// Compute the average length of a list of strings.
extern
void
computeAverage(int
&
amp;average, int
length);
QStringList strings =
...;
QFuture&
lt;int
&
gt; averageWordLength =
QtConcurrent::
mappedReduced(strings, &
amp;QString::
length, computeAverage);
// Create a set of the color distribution of all images in a list.
extern
int
colorDistribution(const
QImage &
amp;string);
QList&
lt;QImage&
gt; images =
...;
QFuture&
lt;QSet&
lt;int
&
gt;&
gt; totalColorDistribution =
QtConcurrent::
mappedReduced(images, colorDistribution,
qOverload&
lt;const
int
&
amp;&
gt;(&
amp;QSet&
lt;int
&
gt;::
insert));
Using Function Objects▲
QtConcurrent::map(), QtConcurrent::mapped(), and QtConcurrent::mappedReduced() accept function objects for the map function. These function objects can be used to add state to a function call:
struct
Scaled
{
Scaled(int
size)
:
m_size(size) {
}
typedef
QImage result_type;
QImage operator
()(const
QImage &
amp;image)
{
return
image.scaled(m_size, m_size);
}
int
m_size;
}
;
QList&
lt;QImage&
gt; images =
...;
QFuture&
lt;QImage&
gt; thumbnails =
QtConcurrent::
mapped(images, Scaled(100
));
Function objects are also supported for the reduce function:
struct
ImageTransform
{
void
operator
()(QImage &
amp;result, const
QImage &
amp;value);
}
;
QFuture&
lt;QImage&
gt; thumbNails =
QtConcurrent::
mappedReduced(images, Scaled(100
), ImageTransform(),
QtConcurrent::
SequentialReduce);
Using Lambda Expressions▲
QtConcurrent::map(), QtConcurrent::mapped(), and QtConcurrent::mappedReduced() accept lambda expressions for the map and reduce function:
QList&
lt;int
&
gt; vector {
1
, 2
, 3
, 4
}
;
QtConcurrent::
blockingMap(vector, [](int
&
amp;x) {
x *=
2
; }
);
int
size =
100
;
QList&
lt;QImage&
gt; images =
...;
QList&
lt;QImage&
gt; thumbnails =
QtConcurrent::
mapped(images,
[&
amp;size](const
QImage &
amp;image) {
return
image.scaled(size, size);
}
).results();
When using QtConcurrent::mappedReduced() or QtConcurrent::blockingMappedReduced(), you can mix the use of normal functions, member functions and lambda expressions freely.
QList&
lt;QImage&
gt; collage =
QtConcurrent::
mappedReduced(images,
[&
amp;size](const
QImage &
amp;image) {
return
image.scaled(size, size);
}
,
addToCollage
).results();
You can also pass a lambda as a reduce object:
QList&
lt;QImage&
gt; collage =
QtConcurrent::
mappedReduced(images,
[&
amp;size](const
QImage &
amp;image) {
return
image.scaled(size, size);
}
,
[](QImage &
amp;result, const
QImage &
amp;value) {
// do some transformation
}
).results();
Wrapping Functions that Take Multiple Arguments▲
If you want to use a map function that takes more than one argument you can use a lambda function or std::bind() to transform it onto a function that takes one argument.
As an example, we'll use QImage::scaledToWidth():
QImage QImage::
scaledToWidth(int
width, Qt::
TransformationMode) const
;
scaledToWidth takes three arguments (including the "this" pointer) and can't be used with QtConcurrent::mapped() directly, because QtConcurrent::mapped() expects a function that takes one argument. To use QImage::scaledToWidth() with QtConcurrent::mapped() we have to provide a value for the width and the transformation mode:
QList&
lt;QImage&
gt; images =
...;
std::
function&
lt;QImage(const
QImage &
amp;)&
gt; scale =
[](const
QImage &
amp;img) {
return
img.scaledToWidth(100
, Qt::
SmoothTransformation);
}
;
QFuture&
lt;QImage&
gt; thumbnails =
QtConcurrent::
mapped(images, scale);