マップとマップリデュースの並行処理

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 を使用する方法の詳細については、QFuture およびQFutureWatcher のドキュメントを参照してください。

シーケンスをインプレースで変更したい場合は、QtConcurrent::map() を使用してください。その場合、map関数は以下の形式でなければなりません:

U function(T &t);

map 関数の戻り値と戻り型は使用されないことに注意してください。

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 を介して結果を返しません。ただし、QFutureQFutureWatcher を使用して、マップの状態を監視することはできます。

コンカレントマップ削減

QtConcurrent::mappedReduced() は、QtConcurrent::mapped() と似ていますが、新しい結果のシーケンスを返す代わりに、reduce 関数を使用して結果を 1 つの値にまとめます。

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

reduce 関数は、map 関数によって返された結果ごとに 1 回呼び出され、中間値を結果変数にマージします。QtConcurrent::mappedReduced() は、一度に reduce を呼び出すスレッドが 1 つだけであることを保証しているので、結果変数をロックするためにミューテックスを使用する必要はありません。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));

関数オブジェクトはreduce関数でもサポートされています:

struct ImageTransform
{
    void operator()(QImage &result, const QImage &value);
};

QFuture<QImage> thumbNails =
        QtConcurrent::mappedReduced(images, Scaled(100), ImageTransform(),
                                    QtConcurrent::SequentialReduce);

ラムダ式の使用

QtConcurrent::map()、QtConcurrent::mapped()、QtConcurrent::mappedReduced() は、map 関数と reduce 関数にラムダ式を使用できます:

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(images,
        [&size](const QImage &image) {
            return image.scaled(size, size);
        },
        [](QImage &result, const QImage &value) {
            // do some transformation
        }
   ).results();

複数の引数をとる関数のラップ

複数の引数をとる map 関数を使いたい場合は、ラムダ関数やstd::bind() を使って、引数をひとつだけとる関数に変換することができます。

例として、QImage::scaledToWidth() を使ってみましょう:

QImage QImage::scaledToWidth(int width, Qt::TransformationMode) const;

scaledToWidth は 3 つの引数("this" ポインタを含む)を取りますが、QtConcurrent::mapped() は引数を 1 つ取る関数を想定しているため、QtConcurrent::mapped() で直接使用することはできません。QtConcurrent::mapped() で QImage::scaledToWidth() を使用するには、幅と 変換モードの値を指定する必要があります:

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

本ドキュメントに含まれる文書の著作権は、それぞれの所有者に帰属します 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。