并发过滤和过滤-减少

QtConcurrent::filter(),QtConcurrent::filtered() 和 QtConcurrent::filteredReduced() 函数可以并行过滤序列中的项,如 QList。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 对象,而是真正的结果类型(在这种情况下,QStringList 和 QSet)。

并发过滤

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

通过 QFuture 提供了过滤结果的访问。有关如何在您的应用程序中使用 QFuture 的更多信息,请参见 QFuture 和 QFutureWatcher 文档。

如果要在原地修改序列,请使用 QtConcurrent::filter()

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

由于序列在原地被修改,QtConcurrent::filter() 不通过 QFuture 返回任何结果。但是,您仍然可以使用 QFuture 和 QFutureWatcher 来监控过滤的状态。

并发过滤-减少

QtConcurrent::filteredReduced() 与 QtConcurrent::filtered() 类似,但不同之处在于它使用减少函数将结果组合成一个单独的值。

减少函数必须具有以下形式

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

T 是最终结果类型,U 是过滤项的类型。请注意,减少函数的返回值和返回类型不使用。

这样调用 QtConcurrent::filteredReduced()

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

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

减少函数将在过滤器函数保留的每个结果上调用一次,并且应该将 中间 结果合并到 结果 变量中。QtConcurrent::filteredReduced() 保证一次只有一个线程调用减少,因此不需要使用互斥锁锁定结果变量。QtConcurrent::ReduceOptions 枚举提供了一种控制减少顺序的方法。

额外的API功能

使用迭代器而非序列

上述每个函数都有一个变体,它接受迭代器范围而不是序列。您可以使用与序列变体相同的方式使用它们

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, qOverload<>(&QChar::isPrint),
                                                         qOverload<const QChar&>(&QSet<QChar>::insert));

请注意 qOverload 的使用。它用于解决具有多个重载的方法的歧义

此外,请注意,在调用 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<QString>> lowerCase = QtConcurrent::filteredReduced(strings, allLowerCase,
                                                                 qOverload<const QString&>(&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() 接受过滤函数的函数对象。这些函数对象可以用来自定义函数调用中的状态

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

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

    QString m_string;
};

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

还支持对汇总函数使用函数对象

struct StringTransform
{
    void operator()(QString &result, const QString &value);
};

QFuture<QString> fooString =
        QtConcurrent::filteredReduced(strings, StartsWith(QLatin1String("Foo")), StringTransform());

使用Lambda 表达式

QtConcurrent::filter()、QtConcurrent::filtered() 和 QtConcurrent::filteredReduced() 接受过滤和汇总函数的Lambda 表达式

// keep only even integers
QList<int> list { 1, 2, 3, 4 };
QtConcurrent::blockingFilter(list, [](int n) { return (n & 1) == 0; });

// retrieve only even integers
QList<int> list2 { 1, 2, 3, 4 };
QFuture<int> future = QtConcurrent::filtered(list2, [](int x) {
    return (x & 1) == 0;
});
QList<int> results = future.results();

// add up all even integers
QList<int> list3 { 1, 2, 3, 4 };
QFuture<int> sum = QtConcurrent::filteredReduced(list3,
    [](int x) {
        return (x & 1) == 0;
    },
    [](int &sum, int x) {
        sum += x;
    }
);

当使用 QtConcurrent::filteredReduced() 或 QtConcurrent::blockingFilteredReduced() 时,可以自由混合使用普通函数、成员函数和Lambda 表达式

void intSumReduce(int &sum, int x)
{
    sum += x;
}

QList<int> list { 1, 2, 3, 4 };
QFuture<int> sum = QtConcurrent::filteredReduced(list,
    [] (int x) {
        return (x & 1) == 0;
    },
    intSumReduce
);

您还可以将Lambda 作为汇总对象传递

bool keepEvenIntegers(int x)
{
    return (x & 1) == 0;
}

QList<int> list { 1, 2, 3, 4 };
QFuture<int> sum = QtConcurrent::filteredReduced(list,
    keepEvenIntegers,
    [](int &sum, int x) {
        sum += x;
    }
);

封装接受多个参数的函数

如果您要使用需要多个参数的过滤函数,您可以使用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
});

© 2024 Qt 公司有限。本说明书中包含的文档贡献是各自所有者的版权。所提供的文档是根据免费软件基金会发布的 GNU 自由文档许可证版本 1.3 的条款许可的。Qt 和相应的标志是芬兰的 Qt 公司及其在全球的子公司和关联公司的 商标。所有其他商标均为其各自所有者的财产。