QFuture与QFutureWatcher实战:构建响应式多线程进度监控系统

QFuture和QFutureWatcher简介 简要介绍QFuture和QFutureWatcher的功能和用途 阅读详情

1. 为什么需要QFuture和QFutureWatcher

在开发图形界面应用时,我们经常会遇到一个经典问题:如何在不阻塞主线程的情况下执行耗时任务?想象一下,你的应用正在处理一个大文件或者进行复杂计算,如果直接在主线程中执行这些操作,界面就会"卡死",用户点击按钮没反应,进度条也不动——这种体验简直糟透了。

这就是Qt引入QFuture和QFutureWatcher这对黄金搭档的原因。它们就像是你应用中的"任务管家"和"进度播报员"。QFuture负责在后台默默干活,而QFutureWatcher则实时向主线程汇报工作进展。我曾在开发一个图像处理软件时,用它们实现了批量图片转换功能,用户可以在转换过程中随时暂停、取消,还能看到实时进度,体验非常流畅。

2. 核心组件解析

2.1 QFuture:异步任务的容器

QFuture就像是给你的异步任务装了个透明盒子。这个盒子有点特殊:

  • 它不是QObject的子类,所以没有信号槽机制
  • 但能告诉你任务的状态(是否完成、是否取消)
  • 可以获取计算结果(当任务完成后)
  • 还能查询进度(0%~100%)
// 创建一个异步任务
QFuture<void> future = QtConcurrent::run([](){
    // 这里是耗时操作
    for(int i=0; i<100; i++){
        QThread::msleep(50); // 模拟耗时操作
    }
});

2.2 QFutureWatcher:任务状态的广播员

QFutureWatcher解决了QFuture最大的痛点——状态通知。它就像个尽职的体育解说员,实时播报任务进展:

QFutureWatcher<void> watcher;
watcher.setFuture(future);  // 开始监控这个future

// 连接各种信号
connect(&watcher, &QFutureWatcher<void>::progressValueChanged, [](int value){
    qDebug() << "当前进度:" << value << "%";
});

connect(&watcher, &QFutureWatcher<void>::finished, [](){
    qDebug() << "任务完成!";
});

2.3 QFutureInterface:幕后的操控者

你可能不会直接使用它,但知道它的存在很重要。QFutureInterface就像是QFuture的"遥控器",负责:

  • 设置进度值
  • 报告任务开始/结束
  • 传递计算结果
QFutureInterface<void> interface;
interface.reportStarted();
interface.setProgressRange(0, 100);

// 在另一个线程中更新进度
interface.setProgressValue(50);

3. 实战:构建进度监控系统

3.1 基础版:文件处理示例

让我们实现一个真实的文件批量处理系统。这个例子中,我们会:

  1. 扫描指定目录下的所有文件
  2. 计算每个文件的MD5校验和
  3. 实时显示处理进度
// 在头文件中
private:
    QFutureWatcher<QString> m_watcher;
    QFuture<QString> m_future;

// 在实现文件中
void FileProcessor::startProcessing(const QString& dirPath)
{
    // 如果已有任务在运行,先取消
    if(m_watcher.isRunning()) {
        m_watcher.cancel();
        m_watcher.waitForFinished();
    }
    
    // 启动异步任务
    m_future = QtConcurrent::run([this, dirPath](){
        QDir dir(dirPath);
        auto files = dir.entryInfoList(QDir::Files);
        
        QStringList results;
        for(int i=0; i<files.size(); i++){
            if(QThread::currentThread()->isInterruptionRequested())
                break;
                
            auto md5 = calculateMD5(files[i].absoluteFilePath());
            results << md5;
            
            // 更新进度 (0~100)
            emit progressChanged(i*100/files.size());
        }
        return results.join("\n");
    });
    
    // 设置监控
    m_watcher.setFuture(m_future);
    
    // 连接信号
    connect(&m_watcher, &QFutureWatcher<QString>::finished, this, [this](){
        emit processingFinished(m_watcher.result());
    });
}

3.2 进阶版:自定义进度控制

有时候QtConcurrent提供的接口不够灵活,我们需要更细粒度的控制。这时可以直接使用QFutureInterface:

void CustomTask::start()
{
    QFutureInterface<QString> interface;
    interface.reportStarted();
    interface.setProgressRange(0, 100);
    
    // 将future交给watcher监控
    m_watcher.setFuture(interface.future());
    
    // 在另一个线程中执行任务
    std::thread([this, interface]() mutable {
        for(int i=0; i<=10; i++){
            if(interface.isCanceled())
                break;
                
            QThread::msleep(200); // 模拟工作
            
            // 更新进度和状态文本
            interface.setProgressValueAndText(
                i*10, 
                QString("处理阶段 %1/10").arg(i)
            );
        }
        
        interface.reportResult("任务完成");
        interface.reportFinished();
    }).detach();
}

4. 常见问题与解决方案

4.1 任务取消的陷阱

很多开发者会遇到取消任务时程序卡死的问题。关键点在于:

  1. 不要直接调用cancel()后就立即销毁对象
  2. 在线程中要定期检查取消状态
// 正确做法
connect(ui->btnCancel, &QPushButton::clicked, [this](){
    m_watcher.cancel();
    // 等待任务真正停止
    m_watcher.waitForFinished(); 
});

// 在线程中
for(int i=0; i<100; i++){
    if(QThread::currentThread()->isInterruptionRequested())
        break;  // 优雅退出
        
    // ...正常工作...
}

4.2 进度更新不灵敏

如果发现进度条"跳变"而不是平滑变化,可能是因为:

  • 更新频率太高(解决方案:适当降低)
  • 在主线程有其他耗时操作(解决方案:确保主线程畅通)
// 每处理10%才更新一次
if(newProgress - lastProgress >= 10){
    interface.setProgressValue(newProgress);
    lastProgress = newProgress;
}

4.3 内存管理要点

在多线程环境下,内存管理要格外小心:

  1. 确保QObject在正确的线程被创建/销毁
  2. 使用QPointer管理可能被销毁的对象
  3. 跨线程信号传递使用QueuedConnection
// 安全的对象管理
QFutureWatcher<void>* watcher = new QFutureWatcher<void>(this); // 指定父对象

connect(watcher, &QFutureWatcher<void>::finished, watcher, &QObject::deleteLater);

5. 性能优化技巧

5.1 合理设置线程数量

默认情况下,QtConcurrent会使用所有可用的CPU核心。但对于IO密集型任务,这反而会降低性能。

// 限制最大线程数
QThreadPool::globalInstance()->setMaxThreadCount(4);

5.2 批量处理数据

比起逐个处理,批量处理能显著减少线程同步开销:

// 批量处理示例
QList<QImage> images = ...;
QFuture<void> future = QtConcurrent::map(images, [](QImage& img){
    img = img.scaled(800, 600);
});

5.3 使用移动语义减少拷贝

对于大型数据结构,使用std::move避免不必要的拷贝:

QFuture<QString> future = QtConcurrent::run([data=std::move(bigData)](){
    // 处理数据...
});

6. 真实案例:图像处理应用

我曾用这套技术开发过一个专业图像处理软件,核心功能包括:

  • 批量转换图片格式
  • 实时预览处理效果
  • 可暂停/恢复的任务队列

关键实现点:

class ImageProcessor : public QObject
{
    Q_OBJECT
public:
    void addTask(const ImageTask& task) {
        m_tasks.enqueue(task);
        if(!m_watcher.isRunning())
            startNextTask();
    }
    
private slots:
    void onTaskFinished() {
        if(!m_tasks.isEmpty())
            startNextTask();
    }
    
private:
    void startNextTask() {
        auto task = m_tasks.dequeue();
        m_future = QtConcurrent::run(&ImageProcessor::processImage, this, task);
        m_watcher.setFuture(m_future);
    }
    
    QImage processImage(const ImageTask& task) {
        // 实际处理逻辑...
    }
    
    QQueue<ImageTask> m_tasks;
    QFuture<QImage> m_future;
    QFutureWatcher<QImage> m_watcher;
};

这个实现允许用户添加多个任务,自动按顺序处理,同时保持界面响应流畅。通过QFutureWatcher的信号,我们还能实时更新每个任务的进度。

7. 调试技巧

调试多线程程序总是充满挑战。以下是我总结的几个实用技巧:

  1. 线程命名:给线程设置名字,调试时一目了然

    QThread::currentThread()->setObjectName("WorkerThread");
    
  2. 跨线程调用检查:

    Q_ASSERT(QThread::currentThread() == qApp->thread());
    
  3. 信号槽连接类型:

    • 默认是AutoConnection
    • 跨线程务必使用QueuedConnection
  4. 死锁预防:

    • 避免在锁保护区域内发射信号
    • 锁的获取顺序要一致
  5. QFuture调试:

    qDebug() << "Future状态:" 
             << future.isStarted() 
             << future.isFinished() 
             << future.isCanceled();
    

8. Qt6中的新变化

Qt6对并发编程做了一些重要改进:

  1. QPromise类:更现代的异步编程接口

    QPromise<int> promise;
    QFuture<int> future = promise.future();
    
    // 在另一个线程
    promise.start();
    promise.addResult(42);
    promise.finish();
    
  2. 更好的线程池管理:

    auto pool = QThreadPool::globalInstance();
    pool->setStackSize(1024*1024);  // 1MB栈空间
    
  3. 性能优化:Qt6的线程同步原语效率更高

  4. 新的并发算法:如parallelForEach

9. 替代方案比较

虽然QFuture+QFutureWatcher很强大,但Qt还提供了其他多线程方案:

方案适用场景优点缺点
QThread长期运行的任务完全控制线程生命周期需要手动管理
QRunnable短期任务与线程池集成无进度报告
QtConcurrent数据并行处理高级API易用灵活性较低
QFuture需要进度反馈的任务完善的监控机制学习曲线较陡

选择建议:

  • 简单计算:QtConcurrent::run
  • 数据处理流水线:QtConcurrent::map/reduce
  • 复杂控制流程:QFutureInterface
  • 长期后台服务:QThread

10. 最佳实践总结

经过多个项目的实战检验,我总结了以下黄金法则:

  1. 保持主线程畅通:所有UI操作必须在主线程完成
  2. 合理划分任务:单个任务不宜过大或过小
  3. 及时释放资源:任务完成后立即清理
  4. 优雅处理取消:给线程退出的机会
  5. 适度更新UI:进度更新频率控制在30-60FPS
  6. 异常安全:使用RAII管理资源
  7. 线程安全设计:避免共享可变状态
  8. 性能分析:使用QElapsedTimer测量耗时

最后分享一个我常用的调试代码片段,可以快速检查线程问题:

qDebug() << "当前线程:" << QThread::currentThread();
qDebug() << "主线程:" << qApp->thread();
qDebug() << "对象线程:" << this->thread();
【QT Creator学习记录】QTConcurrent,QFuture,QFutureWatcher示例代码 了解QTConcurrent,QFuture,QFutureWatcher都是干啥用的,以及简单使用的代码示例 阅读详情

相关推荐

QFutureWatcher与QFuture:解锁Qt异步编程的进度监控与结果处理

本文深入探讨了QFutureWatcher与QFuture在Qt异步编程中的应用,详细解析了进度监控与结果处理的核心机制。通过实战案例展示了如何利用这对黄金组合解决GUI卡顿问题,实现高效的异步任务处理,包括文件批量处理、异常处理及性能优化技巧。

weixin_29224309的博客 357

【QT】Qt之QFutureWatcher

【QT】Qt之QFutureWatcher 简述 QFuture 表示异步计算的结果,QFutureWatcher 则允许使用信号和槽监视 QFuture,也就是说,QFutureWatcher 是为 QFuture 而生的。 详细描述 QFutureWatcher 提供了有关 QFuture 的信息和通知,使用 setFuture() 函数开始监视一个特定的 QFuture,函数 future() 则返回由 setFuture() 设置的 future。 为了方便,QFuture 的很多函数可以直

qq_43331089的博客 6051

Qt Concurrent框架详解(QFuture、QFutureWatcher)

Qt Concurrent是Qt提供的一个并发编程框架,用于简化多线程和并行计算的开发。它提供了一组易于使用的函数和类,可以方便地在多线程环境下处理并发任务。有以下特点:Qt Concurrent提供了一组高级函数和类,使多线程和并行计算变得简单易用。开发者无需显式地创建和管理线程,而是通过调用Qt Concurrent提供的函数实现并发任务。Qt Concurrent能够根据可用的线程数自动将大的问题拆分成更小的任务,并分配给不同的线程并行执行。这样能够最大程度地利用系统资源,提高并发执行效率。

希望能帮助大家,希望大家多多支持,你们的支持是我前进的动力。 4184

Qt多线程-QFuture 和 QFutureWatcher 的使用

在软件开发中,经常会遇到需要异步执行一系列耗时步骤的场景。当前一个操作失败时,我们希望停止后续操作。

lzqd1314的博客 1357

【QT开发】并发编程管理QFutureWatcher类详解及实战应用

通过 QFutureWatcher,Qt 提供了一种非常方便的方法来监控并管理异步任务。合理应用 QFutureWatcher 提供的方法和信号槽机制,可以高效地完成多线程并发任务。同时,通过结合线程池、自定义任务优先级等高级技巧,能够进一步优化程序性能和响应性。

科技改变人类,技术成就未来 1705

chipseeker Y叔

这一次讲解非常重要的peak注释,注释在ChIPseeker里只需要用到一个函数annotatePeak,它可以满足大家各方面的需求。 输入 当然需要我们上次讲到的BED文件,ChIPseeker自带了5个BED文件,用getSampleFiles()可以拿到文件的全路径,它返回的是个named list,我这里取第4个文件来演示。annotatePeak的输入也可以是GRanges对象,你如果用R做peak calling的话,直接就可以衔接上ChIPseeker了。 require(ChIPseeke

生信小博士的博客 1590

QFutureWatcher & QFuture 使用

QFutureWatcher & QFuture 使用

如梦人身 3208

以太网接口及POE设计

PoE标准 Power Over Ethernet 是有源以太网供电的简称 通过Cat-5电缆供电在IEEE 802.3af以太网供电标准中进行了定义。PoE标准以前限制在几瓦以内,但更新的PoE技术可实现更高的功率。例如,PoE+允许每端口功率高达25 W,而PoE++(四 对以太网供电系统)通过使用现有电缆的所有电线允许70 W到100 W的功率。与此PoE标准并行的是ADI公司定义的专有标准 LTPoE++™,该标准定义高达90W的受电设备(PD)功率规范(请参见表1)。 表1. ADI...

u010783226的专栏 1万+

QFutureWatcher:异步运行监视者

一、描述 QFutureWatcher 提供有关 QFuture 的信息和通知。 示例:开始运行并在完成后获取槽回调: MyClass myObject; QFutureWatcher<int> watcher; connect(&watcher, &QFutureWatcher<int>::finished, &myObject, &MyClass::handleFinished); QFuture<int> futur

友善啊,朋友的博客 5281

数字信号处理实验:信号谱分析与FFT算法的应用

​ (1)给定信号为 ,其中a为42,b为1。理论分析信号 的频率成分。 (2)分别采用采样频率为 , , , 对 进行采样,其中 , , , ,根据是否满足采样定理要求分析四种采样频率下频谱的分布,在满足采样定理情况下做频谱分析。 (3)任意生成两组有限长信号,对信号分别进行直接卷积和FFT算法求卷积,并分析圆周卷积与线性卷积的关系。

m0_56324525的博客 3204

HA集群 执行 hdfs haadmin -transitionToActive 失败

最近把一个单NameNode集群变更为HA集群,过程中都很顺利,nn1和nn2两个NameNode都显示standby状态,但是把nn1或者nn2切换到active状态时就会报错抛异常。 查看master上namenode启动日志,如下: Incompatible clusterID for journal Storage Directory /home/hadoop-zero/bigdat...

qq_34727158的博客 1764

Qt之QFutureWatcher

简述 QFuture 表示异步计算的结果,QFutureWatcher 则允许使用信号和槽监视 QFuture,也就是说,QFutureWatcher 是为 QFuture 而生的。 简述 详细描述 基本使用 更多参考 详细描述 QFutureWatcher 提供了有关 QFuture 的信息和通知,使用 s...

weixin_34162695的博客 1959

ESP32-S3 SPI接口实战:从引脚分配到高速通信配置

本文详细解析了ESP32-S3的SPI接口配置与高速通信优化技巧,从引脚分配到软件配置,再到DMA传输和时钟优化,帮助开发者快速掌握SPI通信技术。特别针对ESP32-S3的SPI2接口进行了实战演示,包括硬件连接和常见问题排查,适用于各类嵌入式开发项目。

weixin_29189373的博客 679

Qt多线程常见方法(二)

在上面的示例中,lengthyTask() 函数模拟了一个耗时操作,QtConcurrent::run() 将该函数放在后台线程中执行。QFutureWatcher 对象用于监视任务的执行状态,当任务完成时,会发出 finished 信号,我们通过连接这个信号来处理任务完成时的逻辑。: 如果有一些耗时的操作,但又不希望阻塞主线程,可以使用QtConcurrent来异步执行这些任务,保持界面的响应性。: QtConcurrent可以管理并发任务的结果,使得在主线程中可以轻松访问任务的执行结果。

weixin_42520034的博客 1675

手把手教你使用双流网络结构(CTSDG)实现图像修复训练和推理,GAN图像修复,含完整代码和数据集,全网最详细教程

手把手教你使用双流网络结构(CTSDG)实现图像修复训练和推理,GAN图像修复,含完整代码和数据集,全网最详细教程。该模型采用生成对抗网络(GAN)架构,由生成器和判别器两大部分组成。生成器采用 双分支编码-解码结构处理图像的纹理与结构信息,并通过双向特征融合(Bi-GFF)和通道特征聚合(CFA)模块提升修复质量;判别器则通过分析图像的纹理和结构来区分真实图像与生成图像。整个模型利用多任务损失函数进行优化,以实现高质量的图像补全。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 939

基于Java+SpringBoot+微信小程序实现奶茶点单系统

再不用担心逢年过节的第一杯奶茶有没有人送了,制作一个奶茶点单系统自己当店主,开怀畅饮,一劳永逸。

商务合作 / 项目定制 / 学习交流。个人vx:lovely_wml 1万+

进度条背后的线程安全艺术:QFutureWatcher信号机制深度解析

本文深入解析了Qt框架中QFutureWatcher的信号机制与线程安全实践,揭示了其如何通过事件循环和信号机制实现跨线程的进度监控。文章详细介绍了QFuture、QFutureInterface和QFutureWatcher的三层架构,以及QFutureCallOutEvent在保证线程安全中的关键作用,为构建高响应性GUI应用提供了底层原理和实用技巧。

open4的博客 157

华为OD机试双机位C卷-敌情监控 (Py/Java/C/C++/Js/Go)

华为OD机试(机考)双机位C卷 【敌情监控】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试 - 敌情监控。

qq_45776114的博客 938

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

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

weixin_46424582的博客 1717

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

【QT】QFuture的使用:多线程与进度条

【QT】QFuture的使用:多线程与进度条 介绍 QFuture 类可以用来获取异步计算的结果(类似 std::future ),一般配合 Qt Concurrent 模块和 QFutureWatcher 类工作。在 Qt Creator 中搜索 concurrent 可以看到一些相关示例。官方示例中, QFuture 一般和 QFutureWatcher 配合,因为 QFuture 不是 QObject 子类,没有信号槽。但是 QFuture 相关的接口会触发 QFutureCallOutEvent 事

qq_43331089的博客 2231

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

QT高级线程API总结(五)QFutureWatcher

1、Header: #include <QFutureWatcher> 2、qmake: QT += core 3、描述 QFutureWatcher类允许使用信号和插槽监视QFuture。 QFutureWatcher提供关于QFuture的信息和通知。使用setFuture()函数开始观察特定的QFuture。future()函数使用setFuture()返回future结果集合。 为了方便起见,QFuture的几个函数也可以在QFutureWatcher中使用:progress.

qq_36939187的博客 3837

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Qt Future / QFutureWatcher 实战:不要再用 future.waitForFinished() 卡主线程

这段代码是不是运行在 GUI 主线程?如果是,而且可能耗时,那么就应该警惕。this,[&]() {// 更新UI});QFuture 负责表示异步任务,QFutureWatcher 负责监听任务状态,GUI 线程负责响应结果。并不是洪水猛兽。别在 GUI 主线程里傻等。让耗时工作去后台,让主线程继续处理事件,这才是 Qt GUI 程序应该有的姿势。

qq_40170041的博客 161

qt多线程

【代码】qt多线程。

weixin_46430763的博客 653

Qt 多线程:moveToThread、QtConcurrent、QFutureWatcher 用法整理

qDebug() << “收到参数:” << param << " 线程:" << QThread::currentThreadId();// ==================== moveToThread 标准使用 ====================qDebug() << “工作线程:” << QThread::currentThreadId();qDebug() << “并发线程:” << QThread::currentThreadId();// 不能指定父对象!

u010359310的博客 283

Qt程序设计:QPromise/QFuture多线程任务管理

Qt6 在 QFutureInterface 基础上进一步封装出了 QPromise 类,搭配 QFuture 使用,逻辑类似 std::promise + std::future。使用 QFuture 的优势在于,搭配 QFutureWatcher 使用时可以获取 started / finished 等相关信号,这是 QFutureInterface 内部生成的事件被 QFutureWatcher 接收后发出的。

龚建波 789
上一篇: 卡方检验实战指南:从理论到数据分析应用
下一篇: SpringBoot+Resilience4j构建高可用API:动态限流策略实战
人间清醒863
博客等级 码龄3年 33粉丝 1544原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值