Concurrent Filter and Filter-Reduce

The QtConcurrent::filter(), QtConcurrent::filtered() 和 QtConcurrent::filteredReduced() 函数通过并行计算过滤容器(如 QListQVector) 中每个元素. QtConcurrent::filter() 直接修改容器, QtConcurrent::filtered() 返回包含已过滤元素的新容器, QtConcurrent::filteredReduced() 返回单个结果.

这些函数是 Qt Concurrent 框架的一部分.

上述函数都有一个对应的阻塞函数, 直接返回最终结果, 而不是 QFuture. 使用同步函数的方式与异步函数相同.


  QStringList strings = ...;

  // each call blocks until the entire operation is finished
  QStringList lowerCaseStrings = QtConcurrent::blockingFiltered(strings, allLowerCase);

  QtConcurrent::blockingFilter(strings, allLowerCase);

  QSet<QString> dictionary = QtConcurrent::blockingFilteredReduced(strings, allLowerCase, addToDictionary);

注意: 上述代码的返回结果不是 QFuture 对象, 而是真正的类型结果 (QStringListQSet<QString>).

Concurrent Filter

QtConcurrent::filtered() 的参数是一个序列容器和一个过滤函数. 这个函数为容器中的每个元素调用过滤函数, 返回一个包含过滤值的新序列.

过滤函数形式必须如下:


  bool function(const T &t);

T必须与序列容器中的元素类型一致. 如果过滤函数返回 true, 容器保留元素; 返回false, 容器删除元素.

下列示例显示如何筛选 QStringList中所有的小写字母:


  bool allLowerCase(const QString &string)
  {
      return string.lowered() == string;
  }

  QStringList strings = ...;
  QFuture<QString> lowerCaseStrings = QtConcurrent::filtered(strings, allLowerCase);

filter的计算结果可以通过 QFuture获取. 详见 QFuture , QFutureWatcher.

如果你想修改容器序列, 使用 QtConcurrent::filter():


  QStringList strings = ...;
  QFuture<void> future = QtConcurrent::filter(strings, allLowerCase);

注意: 上述函数的返回值和返回类型未使用.

QtConcurrent::filter() 直接修改容器元素, 没有通过 QFuture返回任何值. 然而, 你仍然可以使用 QFutureQFutureWatcher 监控filter()状态.

Concurrent Filter-Reduce

QtConcurrent::filteredReduced() 与 QtConcurrent::filtered()类似, 但是使用新的类型结果代替容器, 结果是使用reduce函数组合成一个简单值.

reduce函数必须如下形式:


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

T类型是最终结果, U是函数的返回类型. 注意: reduce函数的返回值和返回类型未使用.

如下方式调用 QtConcurrent::filteredReduced():


  void addToDictionary(QSet<QString> &dictionary, const QString &string)
  {
      dictionary.insert(string);
  }

  QStringList strings = ...;
  QFuture<QSet<QString> > dictionary = QtConcurrent::filteredReduced(strings, allLowerCase, addToDictionary);

对于filter函数返回的每个结果, reduce函数被调用一次, 并将中间值合并到结果变量中. QtConcurrent::filteredReduced()保证每次只有一个线程调用reduce, 因此不需要使用互斥锁锁定结果变量. QtConcurrent::ReduceOptions 枚举类型指明调用reduce函数顺序.如果使用QtConcurrent::UnorderedReduce(默认), 调用顺序未定义.QtConcurrent::OrderedReduce表明以容器的元素顺序调用reduce函数.

Additional API Features

使用迭代器代替容器

上述的每个函数都有一个对应的迭代器函数, 使用迭代器范围代替容器. 使用它们的方式与容器参数一样:


  QStringList strings = ...;
  QFuture<QString> lowerCaseStrings = QtConcurrent::filtered(strings.constBegin(), strings.constEnd(), allLowerCase);

  // filter in-place only works on non-const iterators
  QFuture<void> future = QtConcurrent::filter(strings.begin(), strings.end(), allLowerCase);

  QFuture<QSet<QString> > dictionary = QtConcurrent::filteredReduced(strings.constBegin(), strings.constEnd(), allLowerCase, addToDictionary);

使用成员函数

QtConcurrent::filter(), QtConcurrent::filtered(), QtConcurrent::filteredReduced() 接受成员函数的指针. 这个成员函数的类型必须与容器元素类型一致:


  // keep only images with an alpha channel
  QList<QImage> images = ...;
  QFuture<void> alphaImages = QtConcurrent::filter(images, &QImage::hasAlphaChannel);

  // retrieve gray scale images
  QList<QImage> images = ...;
  QFuture<QImage> grayscaleImages = QtConcurrent::filtered(images, &QImage::isGrayscale);

  // create a set of all printable characters
  QList<QChar> characters = ...;
  QFuture<QSet<QChar> > set = QtConcurrent::filteredReduced(characters, &QChar::isPrint, &QSet<QChar>::insert);

注意: 使用QtConcurrent::filteredReduced()时, 你可以自由混合使用普通函数和成员函数:


  // can mix normal functions and member functions with QtConcurrent::filteredReduced()

  // create a dictionary of all lower cased strings
  extern bool allLowerCase(const QString &string);
  QStringList strings = ...;
  QFuture<QSet<int> > averageWordLength = QtConcurrent::filteredReduced(strings, allLowerCase, QSet<QString>::insert);

  // create a collage of all gray scale images
  extern void addToCollage(QImage &collage, const QImage &grayscaleImage);
  QList<QImage> images = ...;
  QFuture<QImage> collage = QtConcurrent::filteredReduced(images, &QImage::isGrayscale, addToCollage);

使用函数对象

QtConcurrent::filter(), QtConcurrent::filtered(), QtConcurrent::filteredReduced() 接受函数对象, 这些函数对象用于为函数调用增加状态记录. result_type 必须通过 typedef 定义为函数的返回类型:


  struct StartsWith
  {
      StartsWith(const QString &string)
      : m_string(string) { }

      typedef bool result_type;

      bool operator()(const QString &testString)
      {
          return testString.startsWith(m_string);
      }

      QString m_string;
  };

  QList<QString> strings = ...;
  QFuture<QString> fooString = QtConcurrent::filtered(images, StartsWith(QLatin1String("Foo")));

使用多个参数的包装函数

如果你想使用一个包含多个参数的过滤函数, 可以使用lambda函数或 std::bind() 将其转换为包含一个参数的函数.

例如, 我们想要使用 QString::contains()作为过滤函数:


  bool QString::contains(const QRegularExpression &regexp) const;

QString::contains() 函数接受2个参数 (包括 "this" 指针), 不能直接作为QtConcurrent::filtered() 的过滤函数, 因为 QtConcurrent::filtered() 只接受一个参数的函数. 想要使用 QString::contains() 作为 QtConcurrent::filtered()的过滤函数, 我们必须为参数 regexp 提供默认值:


  QStringList strings = ...;
  QFuture<QString> future = QtConcurrent::filtered(list, [](const QString &str) {
      return str.contains(QRegularExpression("^\\S+$")); // matches strings without whitespace
  });