歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Qt學習筆記之Concurrent Map和Map-Reduce

Qt學習筆記之Concurrent Map和Map-Reduce

日期:2017/3/1 9:13:28   编辑:Linux編程

簡述

QtConcurrent::map()、QtConcurrent::mapped()和QtConcurrent::mappedReduced()函數在一個序列中(例如:QList或QVector)的元素上並行地運行計算。QtConcurrent::map()直接修改一個序列,QtConcurrent::mapped()返回一個包含修改內容的新序列,QtConcurrent::mappedReduced()返回一個單獨的結果。

這些函數是Qt之Concurrent框架的一部分。

上述每個函數都有一個blocking變量,其返回最終結果而不是一個QFuture。以和異步變量同樣的方式來使用它們。

QList<QImage> images = ...;

// 每一個都調用blocks,直到整個操作完成
QList<QImage> future = QtConcurrent::blockingMapped(images, scaled);

QtConcurrent::blockingMap(images, scale);

QImage collage = QtConcurrent::blockingMappedReduced(images, scaled, addToCollage);

注意:上述的結果類型不是QFuture對象,而是實際結果類型(在這種情況下,QList<QImage>和QImage)。

  • 簡述
  • Concurrent Map
  • Concurrent Map-Reduce
  • 附加API功能
    • 使用迭代器而不是序列
    • Blocking變量
    • 使用成員函數
    • 使用函數對象
    • 使用綁定函數參數
  • 更多參考

Concurrent Map

QtConcurrent::mapped()接受一個輸入序列和map函數,該map函數被序列中的每個元素調用,返回一個包含map函數返回值的新序列。

map函數必須是下面的形式:

U function(const T &t);

T和U可以是任意類型(它們甚至可以是同一類型),但是T必須匹配存儲在序列中的類型,函數返回修改或映射的內容。

下面示例介紹了如何為序列中的所有元素都應用scale函數:

QImage scaled(const QImage &image)
{
    return image.scaled(100, 100);
}

QList<QImage> images = ...;
QFuture<QImage> thumbnails = QtConcurrent::mapped(images, scaled);

map的結果通過QFuture可用。查看QFuture和QFutureWatcher的文檔,了解更多關於程序中如何使用QFutured的內容。

如果想直接修改一個序列,使用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返回任何結果。然而,你仍然可以使用QFuture和QFutureWatcher監控map的狀態。

Concurrent Map-Reduce

QtConcurrent::mappedReduced()類似於QtConcurrent::mapped(),但是返回一個新結果序列,通過一個reduce函數,結果被組合成一個值。

reduce函數必須是以下形式:

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

T是最終結果的類型,U是map函數的返回類型。注意: 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函數返回的每個結果調用一次,並且應該合並中間體到結果變量。QtConcurrent::mappedReduced()可以保證保證一次只有一個線程調用reduce,所以沒有必要用一個mutex鎖定結果變量。QtConcurrent::ReduceOptions枚舉提供了一種方法來控制reduction完成的順序。如果使用了QtConcurrent::UnorderedReduce(默認),順序是不確定的;而QtConcurrent::OrderedReduce確保reduction按照原始序列的順序完成。

附加API功能

使用迭代器而不是序列

上述每個函數都有一個變量,需要一個迭代器范圍,而不是一個序列。以和序列變量同樣的方式來使用它們。

QList<QImage> images = ...;

QFuture<QImage> thumbnails = QtConcurrent::mapped(images.constBegin(), images.constEnd(), scaled);

// map直接僅運行在non-const迭代器上
QFuture<void> future = QtConcurrent::map(images.begin(), images.end(), scale);

QFuture<QImage> collage = QtConcurrent::mappedReduced(images.constBegin(), images.constEnd(), scaled, addToCollage);

Blocking變量

上述每個函數能有一個blocking變量,其返回最終結果而不是一個QFuture。以和異步變量同樣的方式來使用它們。

QList<QImage> images = ...;

// 每一個都調用blocks,直到整個操作完成
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()接受指向成員函數的指針,成員函數類類型必須匹配存儲在序列中的類型:

// 擠壓所有的字符串到一個QStringList中
QStringList strings = ...;
QFuture<void> squeezedStrings = QtConcurrent::map(strings, &QString::squeeze);

// 交換一個列表中的圖片所有像素的rgb值
QList<QImage> images = ...;
QFuture<QImage> bgrImages = QtConcurrent::mapped(images, &QImage::rgbSwapped);

// 創建一個列表中所有字符串長度的集合  
QStringList strings = ...;
QFuture<QSet<int> > wordLengths = QtConcurrent::mappedReduced(strings, &QString::length, &QSet<int>::insert);

注意:當使用QtConcurrent::mappedReduced()時,你可以自由組合正常函數和成員函數的使用:

// 可以使用QtConcurrent::mappedReduced()組合正常函數和成員函數

// 計算字符串列表的平均長度
extern void computeAverage(int &average, int length);
QStringList strings = ...;
QFuture<int> averageWordLength = QtConcurrent::mappedReduced(strings, &QString::length, computeAverage);

// 創建一個列表中所有圖片顏色分布的集合
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()接受函數對象,可用於添加狀態函數調用。result_type typedef必須定義函數調用操作的結果類型:

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

使用綁定函數參數

如果你想使用一個map函數,它接受多個參數可以使用std::bind()來將其轉換到一個接受一個參數的函數上。如果c++ 11支持不可用,boost::bind()或std::tr1::bind()是合適的替代品。

舉個例子,我們將使用QImage::scaledToWidth():

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

scaledToWidth接收三個參數(包括“this”指針),不能直接與QtConcurrent::mapped() 一起使用,因為QtConcurrent::mapped()需要接受一個參數的函數。為了使用QImage::scaledToWidth() with QtConcurrent::mapped(),我們必須為width和transformation mode提供一個值:

std::bind(&QImage::scaledToWidth, 100, Qt::SmoothTransformation)

std::bind()的返回值是一個具有以下簽名的函數對象(functor):

QImage scaledToWith(const QImage &image)

這符合QtConcurrent::mapped()期望的,完整的示例變為:

QList<QImage> images = ...;
QFuture<QImage> thumbnails = QtConcurrent::mapped(images, std::bind(&QImage::scaledToWidth, 100 Qt::SmoothTransformation));

Copyright © Linux教程網 All Rights Reserved