首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >组合QtConcurrent调用

组合QtConcurrent调用
EN

Stack Overflow用户
提问于 2018-04-12 16:55:14
回答 4查看 898关注 0票数 3

我有三个方法,它们都返回一个字符串,我希望使用QtConcurrent运行所有方法,并将它们返回到一个列表或类似的列表中。QtConcurrent::mapped是理想的,因为它返回一个迭代器,但我只能同时运行一个方法。在JavaScript中有promise.all([method_a, method_b, method_c]),它将自动将它们的返回合并到一个结果(迭代器)中。如何在Qt中做到这一点?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2018-04-13 18:02:24

因为要调用几个方法,所以可以将它们作为函子序列传递给QtConcurrent::mapped。映射函子将是一个apply函子,它接受一个函子代表方法呼叫,并传回调用它的结果。

首先,让我们来上课:

代码语言:javascript
复制
// https://github.com/KubaO/stackoverflown/tree/master/questions/concurrent-combine-49802153
#include <QtConcurrent>
#include <functional>
#include <initializer_list>
#include <type_traits>

class Cls {
public:
   QString method1() const { return QStringLiteral("10"); }
   QString method2() const { return QStringLiteral("20"); }
   QString method3() const { return QStringLiteral("30"); }
};

apply_t函子调用传递给它的方法作为参数:

代码语言:javascript
复制
template <class Method> struct apply_t {
   using result_type = typename std::result_of_t<Method()>;
   auto operator()(Method method) {
      return method();
   }
};

让我们方便地从要调用的函子序列的类型中生成这样的应用程序:

代码语言:javascript
复制
template <class Sequence, class A = apply_t<typename std::decay_t<Sequence>::value_type>>
A make_apply(Sequence &&) { return {}; }

为了方便起见,我们还将根据例如make_unique等的精神,使用一个向量生成器:

代码语言:javascript
复制
template <class T> QVector<T> make_vector(std::initializer_list<T> init) {
   return {init};
}

然后,问题变得相当简单。首先,我们创建一个有界方法的向量,这些方法将被调用。然后,我们将调用的方法以及操作它们的应用程序传递给QtConcurrent::mappedresults()按顺序给出了方法调用的所有结果的列表。

代码语言:javascript
复制
int main() {
   Cls obj;
   auto const methods = make_vector({
                                 std::bind(&Cls::method1, &obj),
                                 std::bind(&Cls::method2, &obj),
                                 std::bind(&Cls::method3, &obj)
                              });
   QFuture<QString> result =
         QtConcurrent::mapped(methods, make_apply(methods));
   Q_ASSERT((result.results() == QStringList{"10", "20", "30"}));
}

我们可以使用lambda来提供result_type成员类型,而不是定制的result_type类,这是QtConcurrent::mapped所期望的。有关包装lambda的详细信息,请参阅这个答案其余的答案提供了此类包装的示例。

票数 2
EN

Stack Overflow用户

发布于 2018-04-13 09:56:51

由于没有内置的方法来这样做,所以您可以自己设计一个类来保持未来,并在所有任务完成后返回一组结果。这里唯一的限制是c++的强类型性质:QtConcurrent::run返回的每个未来都保存被调用的函数结果,其类型在编译时给出,作为QFuture模板参数。如果被调用函数的返回类型彼此不同怎么办?在我所提供的示例中,它们都返回相同的类型,但我认为可以使用QVariant来实现这个目的,并将其置之不理。

在一份承诺中

代码语言:javascript
复制
#ifndef PROMISE_H
#define PROMISE_H

#include <QtConcurrent/QtConcurrentRun>
#include <QFutureWatcher>

class PromiseInterface
{
public:
    virtual ~PromiseInterface() = default;
    virtual void finished(int id) = 0;
};

class Watcher : public QObject
{
    Q_OBJECT
    int _id;
    PromiseInterface * _promise;

public slots:
    void finished()
    {
       _promise->finished(_id);
       deleteLater();
    }

public:
    Watcher(int id, PromiseInterface * promise)
        : _id(id),
          _promise(promise)
    {}
};

template <typename T>
class Promise : public PromiseInterface
{
    friend class Watcher;
    void finished(int id) override
    {
        _resolved++;
        _results[id] = _watchers[id]->result();
        delete _watchers[id];

        if(_resolved == _results.size())
        {
            if(_callback != nullptr)
            {
                _callback(_results);
            }
        }
    }
    QList<QFutureWatcher<T> *> _watchers;
    QVector<T> _results;
    void (*_callback)(QVector<T>);
    int _resolved;
public:
    Promise(QList<QFuture<T>> futures)
    {
        _resolved = 0;
        _callback = nullptr;
        _results.resize(futures.size());

        int i=0;
        for(auto f : futures)
        {
            QFutureWatcher<T> * watcher = new QFutureWatcher<T>();
            watcher->setFuture(f);
            QObject::connect(watcher, &QFutureWatcher<T>::finished, new Watcher(i++, this), &Watcher::finished);
            _watchers.append(watcher);
        }
    }
    void then(void (*callback)(QVector<T>)) { _callback = callback; }
};

#endif // PROMISE_H

Promise类是一个类模板,具有一个与QFuture的参数匹配的模板参数。观察到的期货在构造函数中传递,而then方法接受一个完成回调作为它的唯一参数。

QFutureWatcher::finished' signals. Each instance knows the promise object through a pointer to itsPromiseInterfaceand will callfinished`类提供了一个从插槽捕获Watcher的槽,传递的是将来完成的id。

当所有期货完成后,回调函数将被调用,并传递结果向量。

在一个非常简单的使用示例中,我们可以并发执行此函数:

代码语言:javascript
复制
#include <unistd.h>
int f(int r) { sleep(1); return r;}

并将此回调传递给承诺then

代码语言:javascript
复制
void callback(QVector<int> results)
{
    qDebug() << results;
}

我们的主要业务:

代码语言:javascript
复制
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QList<QFuture<int>> futures = {
        QtConcurrent::run(&f, 1),
        QtConcurrent::run(&f, 2),
        QtConcurrent::run(&f, 3)
    };

    Promise<int> promise(futures);
    promise.then(callback);

    return a.exec();
}

大约一秒钟后,这就是预期的输出:

代码语言:javascript
复制
QVector(1, 2, 3)

万一有人想知道我为什么要放入三个类,而不是让Promise直接扩展QObject,并实现finished插槽本身: Qt不允许我这样做。当将Q_OBJECT宏添加到类模板时,将提示一个显式编译器错误: Q_OBJECT不支持的模板类。

票数 2
EN

Stack Overflow用户

发布于 2018-04-12 17:05:52

你要找的方法是QFuture::results()

QList<T> QFuture::results() const 返回未来的所有结果。如果结果不能立即可用,则此函数将阻止并等待它们可用。

从Qt自己的示例扩展

代码语言:javascript
复制
QImage scaled(const QImage &image)
{
   return image.scaled(100, 100);
}

QList<QImage> images = ...;
QList<QImage> thumbnails = QtConcurrent::mapped(images, scaled).results();
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49802153

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档