zoukankan      html  css  js  c++  java
  • 使用QFuture类监控异步计算的结果

    在Qt中,为我们提供了好几种使用线程的方式,除了最基本的QThread类之外,还有QRunnable抽象类,类似于Java的runnable接口,还可以使用moveToThread() 函数,还有更高级的QtConcurrent框架。而今天,我们要看的QFuture就是和QtConcurrent框架API配合使用的一个类。新来看Qt帮助文档对这个类的详细介绍。

    QFuture类用来表示一个异步计算的结果,而该异步计算通常就是由Qt Concurrent框架中的相关函数开启的。QFuture允许线程同步的获得一个或多个在将来某个时间点才准备好的结果。这些结果可以是任何具有默认构造函数和拷贝构造函数的类型。如果在调用该类的result()、resultAt() 或 results() 函数时,某个结果还不可用,QFuture会等待直到该结果可用,即可得到。当然,你也可以使用isResultReadyAt() 函数来判断某个结果是否已经准备好了。对于那些要获得多个结果的QFuture对象来说,resultCount() 函数会返回可以得到的连续结果的个数。这也意味着,我们在0和resultCount() 之间对QFuture对象进行遍历,总是安全的。并且,处理使用刚才说的下标外,QFuture还提供了Java类型的和STL类型的迭代器供我们使用。

    使用QFuture对象,我们也可以和正在运行中的异步计算进行交互。例如,可以使用cancel() 函数取消一个异步计算;使用setPaused()、pause()、resume()、或者 togglePaused() 函数暂停一个异步计算。但要记住,并不是所有的异步计算都能被取消或暂停。例如,QtConcurrent()::run() 方法返回的future不能被取消,但是QtConcurrent::mappedReduced() 函数返回的future就可以。

    除了刚才说的暂停操作,我们还可以获得异步计算的当前进度信息,progressValue()、progressMinimum()、progressMaximum()和progressText() 函数可以帮我们提供这些信息。

    一个比较重要的函数就是waitForFinished(),该函数会导致调用线程阻塞来等待异步计算结束,以确保所有的结果都是可用的。

    上面讲到,我们可以手工的暂停或取消一个异步计算,相对的,QFuture也为我们提供了查询当前计算状态的方法。比如,isCanceled(),isStarted(),isFinished(),isRunning()和 isPaused()函数。

    QFuture是一个轻量级的引用计数类,所以它可以被当做参数值传递。

    其实,在构建QFuture的对象时,我们会同时指定一个模板参数,表示QFuture要处理的结果的类型。其中,QFuture<void>是一个特化的不包含获取结果函数的QFuture对象。内可以将一个QFuture<void>对象赋值给一个QFuture<T>对象,反过来亦可。这对于那些只关注异步计算的状态或进度信息,而不关注实际结果的QFuture对象来说是及其有用的。

    但这个类不支持信号和槽,若想使用信号和槽来和异步计算进行通信,需要使用QFutureWatcher类。

    下面,我们再来看一下QFuture类中常用的一些函数。

    QFuture::QFuture()
    QFuture::QFuture(const QFuture &other)
    QFuture::~QFuture()
    构造函数到没什么好说的,一般使用第一个就好,构造一个QFuture对象,然后用该对象接收QtConcurrent::run() 或 QtConcurrent::mappedReduced()之类的函数的返回值即可。
    但我们要记住,当QFuture对象析构时,既不会等待也不会取消异步计算。我们应该使用waitForFinished() 或者 QFutureSynchronizer类还确保在该对象析构之前所有的异步计算都完成。


    void QFuture::cancel()
    取消该对象代表的异步计算。但请注意,该取消动作也是异步的。如果想同步等待取消完成,要在调用cancel() 函数后,调用一下waitForFinished() 函数。
    被取消的future对象,我们仍然可以从中提取到已经可用的结果,但是,在调用cancel() 之后,不会再有新的结果变的可用,而该future对象上的所有QFutureWatcher对象都不会在向我们传送进度信息和结果可用的信号。

    再次重申,不是所有的异步计算都可以取消。参看上面已经说过的。


    bool QFuture::isCanceled() const
    判断异步计算是否被cancel() 函数取消了,但要注意,我们在上面讲cancel() 函数的时候就说过,cancel动作是异步,也就是说当这个函数返回true的时候,异步计算可能还在运行。
    void QFuture::pause()
    暂停当前future对象代表的异步计算。等价于setPaused(true)
    bool QFuture::isPaused() const
    判断一个异步计算是否被pause() 函数暂停了。同样,暂停动作也是异步的。

    至于其他的成员方法,也都非常容易理解,见名知意,大家在使用时,具体参考Qt帮助文档即可。
    上面我们说到,QFuture在取得异步结果方面除了提供了相关的result() 函数,还提供了方便的迭代器类,既有STL风格的QFuture::const_iterator,也有Java风格的QFutureIterator类。其中,STL风格的迭代器和STL中容器的迭代器一样,重载了*和->运算符,以及其他常用的++,--等运算符。而Java风格的迭代器则提供了hasNext()、next()等函数。下面我们分别来看看。

    STL风格的迭代器,即QFuture::const_iterator,我们可以定义一个该类型的变量,然后使用QFuture::constBegin() 进行初始化。如下代码所示:

    QFuture<QString> future = ...;

    QFuture<QString>::const_iterator i;
    for (i = future.constBegin(); i != future.constEnd(); ++i)
    cout << *i << endl;
    同样,对于Java风格的迭代器来说,由于其是一个c++类,所以其构造函数接受一个要进行迭代的QFuture对象,然后使用next() 函数进行逐个遍历。例如以下代码所示:

    QFuture<QString> future;
    ...
    QFutureIterator<QString> i(future);
    while (i.hasNext())
    qDebug() << i.next();
    除了next()方法外,QFutureIterator类还提供了previous() 函数,可以完成从后想起的反向遍历。如下代码所示:

    QFutureIterator<QString> i(future);
    i.toBack();
    while (i.hasPrevious())
    qDebug() << i.previous();

    下面,我们就来写一个使用QFuture的简单例子。因为我们还没讲到QtConcurrent框架及其API,所以,我们就简单的用一下QtConcurrent::run()方法,来启动一个函数,在此函数中计算第100个斐波那契数。代码如下:

    #include <QCoreApplication>
    #include <QFuture>
    #include <QtConcurrent>
    #include <QDebug>

    //计算第lindex 个 斐波那契数值
    qulonglong Fibonacci(int index)
    {
    qulonglong f1 = 1, f2 = 1, cur = 0;
    for(int i = 3; i <= index; i++)
    {
    cur = f1 + f2;
    f1 = f2;
    f2 = cur;
    }
    return cur;
    }

    int main(int argc, char *argv[])
    {
    QCoreApplication a(argc, argv);

    QFuture<qulonglong> future = QtConcurrent::run(Fibonacci, 100);
    //future.waitForFinished();
    qDebug() << "test";
    qDebug() << future.result();

    return a.exec();
    }

    在此,我们通过使用run()方法,就启动了一个异步计算,紧接着我们调用result() 函数,取得该异步计算的结果。如果此时该结果还不可用,那么调用该函数会是函数阻塞等待结果。当然,我们也可以先调用waitForFinished() 等待其运行结束。其输出结果如下:


    先打印出 “test” 字符串,可见,我们通过run() 启动的Fibonacci() 函数确实是异步执行的,并没有阻塞我们的main函数的执行流程。

    另外,QtConcurrent在Qt中是一个独立的命名空间,所以还要在.pro文件中引入该模块。如下:

    QT += core concurrent


    至于QtConcurrent中的其他函数及其相关类,会在后续博文中讲解。

    再上面的例子中,我们只是使用QtConcurrent::run()开启了一个异步计算,然后调用QFuture::result() 等待计算的结果。但其实同一时间,我们可以同时开启多个并行运行的异步计算,然后分别使用一个QFuture对象等待结果。但其实在Qt中,为了简化多个QFuture的同步等待操作,特地为我们提供了一个模板类QFutureSynchronizer。该类为我们提供了 addFuture()和setFuture()函数,我们可以使用这两个函数,将代表多个异步计算的QFuture对象添加到一个QFutureSynchronizer对象中,然后调用该类的waitForFinished()方法,该方法就是用来等待所有的QFuture结束。

    简单使用例子如下:

    void someFunction()
    {
    QFutureSynchronizer<void> synchronizer;
    synchronizer.addFuture(QtConcurrent::run(anotherFunction));
    synchronizer.addFuture(QtConcurrent::map(list, mapFunction));

    return; // 等待所有的异步计算结束才返回
    }
    除了等待函数外,QFutureSynchronizer类还提供了一setCancelOnWait()方法,若通过这个函数设置了cancel-on-wait标志,那么调用waitForFinished()函数会取消所有未完成的计算。
    下面,我们在上面求斐波那契数的例子中,再开启一个异步计算,同时求出两个斐波那契数

    #include <QCoreApplication>
    #include <QFuture>
    #include <QFutureSynchronizer>
    #include <QtConcurrent>
    #include <QDebug>

    //计算第lindex 个 斐波那契数值
    qulonglong Fibonacci(int index)
    {
    qulonglong f1 = 1, f2 = 1, cur = 0;
    for(int i = 3; i <= index; i++)
    {
    cur = f1 + f2;
    f1 = f2;
    f2 = cur;
    }
    return cur;
    }

    int main(int argc, char *argv[])
    {
    QCoreApplication a(argc, argv);

    QFutureSynchronizer<qulonglong> synchronizer;
    synchronizer.addFuture(QtConcurrent::run(Fibonacci, 50));
    synchronizer.addFuture(QtConcurrent::run(Fibonacci, 100));
    synchronizer.waitForFinished();
    qDebug() << "第50个斐波那契数: " << synchronizer.futures()[0].result();
    qDebug() << "第100个斐波那契数: " << synchronizer.futures()[1].result();

    return a.exec();
    }

    运行结果如下:

    ---------------------
    作者:求道玉
    来源:CSDN
    原文:https://blog.csdn.net/Amnes1a/article/details/65630701
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    Swift扩展(Extension)
    Swift构造器(Initializer)与析构器(Deinitializer)
    Swift下标
    Swift方法
    Swift属性
    Swift类与结构体
    Swift闭包(Closure)
    python调用c++/c 共享库,开发板上编译的一些坑!
    python调用c++类方法(2)
    ubuntu 18.04 gcc g++降级4.8版
  • 原文地址:https://www.cnblogs.com/findumars/p/11161038.html
Copyright © 2011-2022 走看看