QtConcurrent多線程 - map、mapped和mappedReduced


Qt中的 Concurrent 模塊爲我們提供了在一個序列容器類型針對每一個元素的多線程並行運算,比如 QListQVector

  • QtConcurrent::map() :對序列的每一項元素都應用一個函數,並將運算結果替換原來的元素。
  • QtConcurrent::mapped() :功能類似 map() 函數,它會返回一個新容器存儲函數處理後的結果。
  • QtConcurrent::mappedReduced() :類似於 mapped() ,他會將這個返回的結果序列,經過另一個函數處理爲一個單個的值。

1. map 和 mapped

QtConcurrent::mapQtConcurrent::mapped 的基本形式如下:

QFuture<void> map(Sequence &sequence, MapFunctor map)

  • 參數 sequence :表示序列容器(如QList、QVector等)
  • 參數 map :表示函數。函數的形式必須符合 U function(T &t); 形式。
    類型 UT 可以爲任何類型,但是 T 的類型必須與容器中的類型保持一致。返回值 U QtConcurrent::map 函數中並沒有用到。
  • 返回值 QFuture<void> 。關於 QFuture 這裏就不過多介紹了,瞭解更多可以參考上一篇文章 QtConcurrent多線程 - run()與QFuture

下面是一個簡單的使用示例:

void scale(QImage &image)
{
    image = image.scaled(100, 100);
}

QList<QImage> images = ...;
QFuture<void> future = QtConcurrent::map(images, scale);

該示例中,多線程對 QList<QImage> 容器中的每一個 QImage 縮放到 100 * 100 的尺寸。結果會覆蓋掉原容器中的元素。


QtConcurrent::mapped 函數跟 QtConcurrent::map 函數類似,不同之處在於:

  1. 傳遞的函數形式不同。它的形式爲, U function(const T &t); ,一個常引用做爲參數,表示容器中的元素不可修改。返回值 U 存入整個 QtConcurrent::mapped 計算後的新容器中返回。
  2. 函數 QtConcurrent::mapped 的返回值爲 QFuture<U>QFuture 除了可以表示單個類型也可以一組序列容器類型,跟單個的類似,可以使用 results 函數返回運算後的結果。

下面是使用 QtConcurrent::mapped 的例子

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

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

這裏同樣也是對列表中的 QImage 進行縮放到 100 * 100 大小,但是容器本身的內容沒有改變。
使用如下代碼獲取運算結果:

funcFunture2.waitForFinished();		// 等待運算處理完成
thumbnails.results();				// 獲取運算結果

2. mappedReduced

mappedReduced() 類似於 QtConcurrent::mapped() ,但是它不是返回一個帶有新結果的序列,而是使用reduce函數將結果組合成一個單獨的值。

它的函數基本形式如下:

template <typename Sequence, typename MapFunctor, typename ReduceFunctor>
QFuture<typename QtPrivate::ReduceResultType<ReduceFunctor>::ResultType> 
mappedReduced(const Sequence &sequence,
              MapFunctor map,
              ReduceFunctor reduce,
              ReduceOptions options = ReduceOptions(UnorderedReduce | SequentialReduce))
  • 參數 sequence :表示序列容器。
  • 參數 map :同 mapped 函數,形式爲 U function(const T &t); 的函數。
  • 參數 reduce :處理返回後的結果序列的函數。形式爲 V function(T &result, const U &intermediate)T 爲最終結果類型, Umap 函數的返回類型,V mappedReduced 並沒有使用此返回值。
  • 參數 optionsreduce 函數的執行順序,默認爲任意順序。設置爲 OrderedReduce 表示按照原始序列的順序執行。而 SequentialReduce 則表示同一時刻,只有一個線程在執行 reduce 函數,設置爲此標誌時,可以不用對 reduce 函數進行加鎖操作。
  • 返回值: 返回 QFuture<T> ,這個 T 類型就是 reduce 中的第一個參數。

下面是一個示例:

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

這裏 images 中的每一個元素執行完 scaled 函數後的序列,序列中的每一個元素再執行函數 addToCollage ,最後將計算結構返回。


3. 其他擴展

  1. 使用迭代器作爲序列容器的輸入範圍。
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);
  1. 阻塞等待形式
    map 、mapped和mappedReduced爲非阻塞形式的多線程執行,可以使用 QFutureQFutureWatcher 作異步通知。但是Qt同樣也提供了阻塞版本的函數:
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 ,而是真實的結果類型。(在這個示例中,返回 QImageQList<QImage>

  1. 使用類的成員函數
// 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(string, &QString::length, &QSet<int>::insert);
  1. 仿函數
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<QImage>(images,
                                    Scaled(100),
                                    ImageTransform(),
                                    QtConcurrent::SequentialReduce);
  1. lambda或使用std::bind,處理多參數函數

比如這個函數

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

可以藉助 lambda 表達式處理參數

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

作者: douzhq
個人博客主頁:不會飛的紙飛機
微信公衆號: 不會飛的紙飛機 ,不定時更新技術文章和搞笑段子。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章