QFutureWatcher并行计算和异步任务监控


前言

说到QFutureWatcher,就不得不说到Qt Concurrent和QFuture,Qtconcurrent是Qt的高级接口,为什么说是高级呢?因为有的时候,对于一些耗时的运行任务,我们做法往往是直接丢给QThread自己去run,或者搞一个工作类继承QObject,然后movetoThread。当然这些方法并没有错,但是我们只是做一次耗时运算,仅仅一次,开线程未免太过奢侈了吧。。。

那有没有更加优雅的方法呢,当然,Qt早就想到了,那就是Qt Concurrent,它提供了一整套异步运算的高级API,如果有兴趣,你可以查询Qt Assistant,有很详细的介绍,本文只是对异步运算任务进行监控。


提示:以下是本篇文章正文内容,下面案例可供参考

一、QFuture及QFutureWatcher

QFuture类表示异步计算的结果。既然表示结果,那么应该什么时候去获取,以及如何获取结果呢?不要慌,听我慢慢讲,由于QFuture不是继承QObject,即不是QObject的子类,那么也就意味着不能使用信号和槽机制,那就更离谱了,不能用信号槽,而且是异步运算的,那咋整?

答案就是QFutureWatcher,QFutureWatcher类允许使用信号和插槽监控QFuture。
QFutureWatcher提供有关QFuture的信息和通知。使用setFuture()函数开始监视特定的QFuture。函数的作用是:返回带有setFuture()的future集。

1.获取

关于QFuture的创建,目前最新版本的是Qt6.2,据说可以自己创建QFuture对象,博主这边使用的还是Qt5.9,必须使用Qt concurrent的API返回一个QFuture的对象,代码如下:

    QFuture<void> fu = QtConcurrent::run([&](){
        QStringList ned = alist;
        qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")<<"计算开始";
        for(int i =0;i<ned.size();++i){
            QString as = ned.at(i);
            quint32 val = JQChecksum::crc32(as,as.split("/").last());
            qDebug()<<val;
        }
        qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")<<"计算结束";
    });

这是使用run作为异步运算的接口,具体内容由lambda表达式给出。这是一般的写法,如果需要做异步通知的话,可以在lambd最后自定义一个信号发出来,通知外面(主线程)活干完了。但是根据Qt的官方文档,这样说的:
QFuture also offers ways to interact with a runnning computation. For instance, the computation can be canceled with the cancel() function. To pause the computation, use the setPaused() function or one of the pause(), resume(), or togglePaused() convenience functions. Be aware that not all asynchronous computations can be canceled or paused. For example, the future returned by QtConcurrent::run() cannot be canceled; but the future returned by QtConcurrent::mappedReduced() can.

翻译一下:QFuture还提供了与运行计算交互的方法。例如,可以使用cancel()函数取消计算。要暂停计算,请使用setPaused()函数或pause()、resume()或togglePaused()函数之一。请注意,并非所有异步计算都可以取消或暂停。例如,QtConcurrent::run()返回的future不能取消;但是QtConcurrent::mappedReduced()可以

2.使用mappedReduced

代码如下:

void MainWindow::on_pushButton_clicked()
{
    QList<QString> alist;
    QDir dir("E:\\MODLE_PG_SVN_SRC\\bin\\Data_Files\\Patterns\\res1080_2340");
    QFileInfoList infolist  = dir.entryInfoList(QDir::Files);
    qDebug()<<infolist.size();
    for(auto item : infolist){
        alist<<item.absoluteFilePath();
    }

#if 1
    int size = alist.size();
    qDebug()<<"需要计算的图片数:"<<size;
    ui->progressBar->setMaximum(size);
    QThreadPool::globalInstance()->setMaxThreadCount(size>10?10:size);
    QEventLoop loop;
    QFutureWatcher<QList<quint32>> watcher;
    QFuture<QList<quint32>> f = QtConcurrent::mappedReduced(alist,func2,sum);
    connect(&watcher,&QFutureWatcher<QList<quint32>>::finished,[&](){
        showlog("并行计算结束");
        qDebug()<<f.result();
        loop.quit();
        QMessageBox::about(this,"tips","并行计算结束");
    });
    connect(&watcher,&QFutureWatcher<QList<quint32>>::progressValueChanged,[&](int value){
        qDebug()<<"计算进度:"<<QString::number(value);
        ui->progressBar->setValue(value);
    });
    watcher.setFuture(f);
    showlog("开始计算进入loop等待...");
    loop.exec();
    showlog("结束等待...");
#else
    QFuture<void> fu = QtConcurrent::run([&](){
        QStringList ned = alist;
        qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")<<"计算开始";
        for(int i =0;i<ned.size();++i){
            QString as = ned.at(i);
            quint32 val = JQChecksum::crc32(as,as.split("/").last());
            qDebug()<<val;
        }
        qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")<<"计算结束";
    });

#endif
}

简单说明一下,我这边任务时计算图片的CRC值,有多少张呢,大概好几百张,如果根据上述1的方法,for循环来一张张计算,显然速度慢,当然也不是不行,如果你输了少的话,倒是没什么问题。或者说对等待时间没有要求的话,是可以的。

还是解释一下吧,QThreadPool是全局线程池,可以修改每次并行任务计算的线程数,这里修改成10.然后开启一个事件循环,定义一个QFutureWatcher,并且用mappedReduced返回一个QFuture对象,mappedReduced是非阻塞的,如果你要阻塞,可以用带block的。此处就不展开了,可以自行查询。mappedReduced有三个参数,第一个是list列表,第二个是任务函数,第三个是对每一个结果做一个操作的。

原型如下:

QFuture<T> QtConcurrent::mappedReduced(ConstIterator begin, ConstIterator end, MapFunction mapFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce)

官方解释:按顺序为每个项调用mapFunction一次。每个mapFunction的返回值都传递给reduceFunction。

具体定义如下:

quint32 func2(QString a){
    qDebug()<<"current thread ID:"<<QThread::currentThreadId();
    int val = JQChecksum::crc32(a,a.split("/").last());
    return val;
}

void sum(QList<quint32>& L, const quint32& b){
    L<<b;
}

每次计算结束,都将结果存入到QList中,因此所有任务时,可以用QFuture::result()接口获取到这个List

3.进度

    connect(&watcher,&QFutureWatcher<QList<quint32>>::progressValueChanged,[&](int value){
        qDebug()<<"计算进度:"<<QString::number(value);
        ui->progressBar->setValue(value);
    });

QFutureWatcher有个信号progressValueChanged,可以实时传递异步任务完成的数量,不得不感叹,确实强大,我这边是直接绑定到QProgress进度条了。

注意不要忘了,将QFuture对象计入到watcher

watcher.setFuture(f);

4.结果

在这里插入图片描述

总结

以上就是今天要分享的全部内容,本文仅仅简单介绍了QFuture以及QFutureWatcher的使用,其实Qt concurrent还有大量的异步的高级API,继续学习。另外,本文所提内容,也是博主自己的一些实际运用的看法,如有不对的地方,欢迎指正。
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
2022年11月4日-2022年11月14日购买当前课程赠送课程学习地址如下:https://edu.csdn.net/course/detail/32434https://edu.csdn.net/course/detail/35658https://edu.csdn.net/course/detail/30223https://edu.csdn.net/course/detail/32408https://edu.csdn.net/course/detail/32429注:因赠送课程不会出现在已订阅课程列表中,以下课程学习地址一定要收藏保存。#课程服务 在线答疑:本课程设有专门的讨论留言区,学习中遇到任何问题,直接给老师留言即可,老师都会及时进行回复。远程协助:如果遇到复杂问题,老师还可进行远程协助,这个一般可不是一两百元的课程就能享受到的。源码分享:为了让大家更好的进行项目实战,老师还将课程中涉及到的所有源码分享给学员,按照视频中的提示进行下载即可。在CSDN分享C++ Qt开发知识已经有6年了,感谢众多博友对我的支持,了解到很多人对Qt的使用还是有些困扰,例如Qt环境搭建,Qt布局的使用,如何使用Qt编写复杂的界面,如何自定义非标控件,Qt如何和Web交互,Qt和后台接口如何交互等;经过这几年的整理,我决定出这套《Qt高级开发视频教程》,带领大家学习Qt高级开发知识,学习如何使用Qt开发企业级别的项目;通过本课程的学习,大家将会达到企业招聘的中高级要求。为了照顾零基础学员,本课程第一章会介绍Qt环境搭建、QtCreator / VS2019的基本使用方法,Qt整体架构、Qt信号机制,Qt内存管理等知识。即使没有Qt开发的学习经验,也能跟着课程顺利学习。课程核心知识点地图如下: 课程每章核心知识点介绍如下: 第一章:介绍Qt环境搭建、QtCreator / VS2019的基本使用方法,Qt整体架构、Qt信号机制,Qt内存管理等知识。第二章:了解到很多学员对于Qt界面布局很不熟悉,将会详细介绍Qt设计器布局,以及如何C++代码手写布局,从常见的企业级项目入手,带领大家学会各种布局的实现,例如WPS、腾讯会议、优酷、迅雷等界面的实现;界面布局会了,这是企业项目开发的第一步,还有更重要的无边框窗口,如何设计一个合理的无边框窗口很重要,第三/四章:详细介绍如何实现一个无边框窗口,如何自定义标题栏,如何实现拖拽拉伸;第四章将会介绍如何自定义非标控件,优化Qt界面。第五章:介绍Qt web混合编程,一个商用项目,必然会涉及到web交互,这也是很多Qt开发者的弱项,这一章讲详细介绍C++ Qt web混合开发。第六章:既然是做企业级项目,必然需要和后台交互,http编程也是必要的,将详细介绍http编程,用户注册,登录,后台接口请求等知识;通过第五、六章的学习,将会是你的Qt开发技术更上一层楼。第七章:介绍Qt并发编程,耗时任务处理,进程调用等知识。第八、九章:讲解 Qt 比较重要的知识,图形视图结构,以及MVD模式;通过这两章的学习,大家会对图形视图有更好的了解。第十章:本章是独立章节,主要介绍Qt中一些特殊技巧,项目编译,dpi适配、多语言等知识。第十一章:是我们的企业级项目实战:实现一个视频会议客户端,本项目可以进行多人视频通话,直播,桌面分享等功能,本项目我会从零开始,进行项目搭建,功能调试,bug fixed, 带领大家做一个企业级项目。希望通过本课程的学习,大家的C++ Qt开发技术能有质的飞越,能找到自己心仪的工作。课程中如果讲的不对的地方,请大家指出,我及时修正,我也只是一个普通开发者,也不是所有的技术都会,尽我所能,把我所会的教给大家,让我们一起为Qt的发展,尽一份绵薄之力。 下面是本课程一些项目的截图: 1 可以滑动的设置界面         2 所有图形的绘制       3 视频播放器          4 高仿youku界面         5 视频会议         相信通过本课程的学习,大家有能力实现绝大部分客户端项目,从此用C++ Qt再也不会有难写的界面。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值