用c++实现异步线程的进度条
突然想起来好久之前遇到的问题,记录下。
想解决的问题:执行计算时不知道什么时候结束,进度条的值没法精确给出,只能给个假的。另外不能在主线程中执行计算,要开另外的线程计算,不然主界面就卡住了。
解决代码如下:
1 QEventLoop eventLoop; 2 QFutureWatcher<void> futureWatcher; 3 connect(&futureWatcher, &QFutureWatcher<void>::finished, [&]() { 4 if (!futureWatcher.isCanceled()) 5 { 6 g_service->m_pStatusProgressBar->Finish(); 7 if (!noDataFlag) 8 { 9 mapApp->showStatusBarMessage(tr("xx完成!")); 10 QMessageBox::information(nullptr, tr("提示"), tr("xx完成!")); 11 } 12 } 13 eventLoop.quit(); 14 }); 15 connect(&futureWatcher, &QFutureWatcher<void>::progressValueChanged, 16 [&]() { 17 g_service->m_pStatusProgressBar->SetValue(50); 18 }); 19 connect(this, SIGNAL(noData()), this, SLOT(noDataInfo())); 20 g_service->m_pStatusProgressBar->Begin(); 21 mapApp->showStatusBarMessage(tr("正在执行xx...")); 22 23 futureWatcher.setFuture(QtConcurrent::run([&]() 24 { 25 // 导出文件 26 exportFile(Data); 27 })); 28 eventLoop.exec();
简单代码含义:执行exportFile函数过程显示进度条,自己的状态栏显示文字正在执行xx,进度条的值一直是50,结束后弹QMessageBox提示框,状态栏显示xx完成。
代码浅解:
1、QEventLoop
QEventLoop 类提供了一种进入和离开事件循环的方法。
代码中使用了exec()和quit()。下面分别说明函数含义。
int exec(ProcessEventsFlags flags = AllEvents)
进入主事件循环并等待 exit() 被调用。返回传递给 exit() 的值。如果指定了标志,则仅处理标志允许的类型的事件。需要调用这个函数来启动事件处理。
void quit()
事件循环正常退出。等同于exit(0)。
2、QFutureWatcher
QFutureWatcher 允许使用信号和槽监视 QFuture。
QFuture 类表示异步计算的结果,使用 QtConcurrent 框架中的API启用。
但是QtConcurrent仅支持接受纯函数或者lambda表达式,不支持信号和槽,如果需要监听任务执行结果可以通过与QFuture和QFutureWatcher配合来达到。
[signal] void finished()
当监视的 QFuture 结束了,会发出这个信号。
[signal] void progressValueChanged(int progressValue)
当监视的 QFuture 报告进度时发出此信号,progressValue 给出当前进度。
为了避免 GUI 事件循环过载,QFutureWatcher 限制了进度信号发射率。最后一个进度更新(progressValue 等于最大值)将始终交付。
bool isCanceled() const
如果异步计算被取消返回true,否则返回false。注意有时候返回true可能计算仍然在运行。
void setFuture(const QFuture<T> &future)
开始监视QFuture。
3、lambda表达式
lambda表达式是C++11新特性之一。
lambda表达式(也称为lambda函数)是在调用或作为函数参数传递的位置处定义匿名函数对象的便捷方法。
通常,lambda用于封装传递给算法或异步方法的几行代码 。
lambda表达式没有函数名;
本文代码中的lambda表达式看起来如下所示:
[&]() {
code
}
捕获列表总是出现在Lambda函数的开始处。[]是Lambda引出符。
编译器根据该引出符判断接下来的代码是否是Lambda函数,捕获列表能够捕捉上下文中的变量以供Lambda函数使用。
[&]表示引用传递方式捕捉所有父作用域的变量(包括this)。
() 是参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号“()”一起省略。
中间省略了【可变规则】【异常说明】【返回类型】。lambda表达式的更多含义可参考一篇不错的博文:
https://blog.csdn.net/qq_37085158/article/details/124626913
code是lambda函数体。内容与普通函数一样,可以使用参数,还可以使用所有捕获的变量。
4、connect信号与槽
信号(Signal):就是在特定情况下被发射的事件。
槽(Slot):就是对信号响应的函数。槽函数与一般的C++函数是一样的。
[static] QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection);
参数的含义分别是:发送者,发送的信号,接收者,接收者的响应函数,信号与槽的连接方式。
5、QtConcurrent::run
QtConcurrent::run()函数会在一个单独的线程中执行,并且该线程取自全局QThreadPool,该函数的返回值通过QFuture API提供。
该函数可能不会立即运行;函数只有在线程可用时才会运行。
通过QtConcurrent::run()返回的QFuture不支持取消、暂停,返回的QFuture只能用于查询函数的运行/完成状态和返回值。
6、参考网址
https://blog.csdn.net/kenfan1647/article/details/118570966
https://www.jianshu.com/p/2fff70ad81a2
https://blog.csdn.net/qq_37085158/article/details/124626913
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)