当前位置: 首页 > news >正文

QFutureWatcher 收不到 finished 信号-QFutureWatcher 与对象生命周期

Qt 异步任务与对象生命周期的“隐形地雷”

QFutureWatcher 与对象生命周期

窗口关闭时 QFutureWatcher 收不到 finished 信号的两种根治方案

完整代码

class SubWidget : public QMainWindow
{Q_OBJECT
public:SubWidget(QWidget *parent = nullptr);~SubWidget();protected:void closeEvent(QCloseEvent* event) override;
private slots:void onQFutureWatcherFinished();
private:QFutureWatcher<bool>* createWatcher();
private:Ui::SubWidgetClass ui;QPointer<QFutureWatcher<bool>> m_pWatcher;
};
#include <QDebug>
#include <QFuture>
#include <QThread>
#include <QtConcurrent/QtConcurrent>
SubWidget::SubWidget(QWidget *parent): QMainWindow(parent)
{ui.setupUi(this);}SubWidget::~SubWidget()
{/*如果testAttribute(Qt::WA_DeleteOnClose) == true,就等同于在析构函数中调用了m_pWatcher = createWatcher();*/qDebug() << "SubWidget::~SubWidget: destructor called";int debug = 0;
}void SubWidget::closeEvent(QCloseEvent* event)
{QMainWindow::closeEvent(event);createWatcher();/*如果testAttribute(Qt::WA_DeleteOnClose) == true,就等同于在析构函数中调用了m_pWatcher = createWatcher();	*/// 
#if 解决方案一m_pWatcher = createWatcher();if (m_pWatcher != nullptr && m_pWatcher->isRunning()){qDebug() << "SubWidget::closeEvent: m_pWatcher is running, waiting for it to finish";m_pWatcher->waitForFinished();qDebug() << "SubWidget::closeEvent: waiting for it to finished";int debug = 0;}
#endif
}void SubWidget::onQFutureWatcherFinished()
{qDebug() << "QObject::sender():" << QObject::sender()->objectName();auto pWatcher = dynamic_cast<QFutureWatcher<bool> *>(sender());if (pWatcher == nullptr){qDebug() << "onQFutureWatcherFinished: pWatcher is nullptr";return;}qDebug() << "onQFutureWatcherFinished: pWatcher is not nullptr";pWatcher->deleteLater();
}QFutureWatcher<bool>* SubWidget::createWatcher()
{QFutureWatcher<bool>* pWatcher = new QFutureWatcher<bool>;pWatcher->setObjectName(QString("pWatcher_SubWidget_%1").arg(this->testAttribute(Qt::WA_DeleteOnClose)));connect(pWatcher, &QFutureWatcher<bool>::finished, this, &SubWidget::onQFutureWatcherFinished);//解决方案二
#if 1connect(pWatcher, &QFutureWatcher<bool>::finished, [pWatcher]() {qDebug() << "QFutureWatcher finished";if (pWatcher == nullptr){qDebug() << "QFutureWatcher is nullptr";return;}qDebug() << "QObject::sender():" << pWatcher->objectName();auto re = pWatcher->result();int debug = 0;});
#endifpWatcher->setFuture(QtConcurrent::run([=]() -> bool {qDebug() << "Running in a separate thread";QThread::sleep(3); return true; }));return	pWatcher;
}

使用

	auto pSubWidget = new SubWidget(this);pSubWidget->setAttribute(Qt::WA_DeleteOnClose, true);pSubWidget->show();

现象回放

1.为什么在 closeEvent 里启动任务却永远等不到 finished?

  • Qt::WA_DeleteOnClose 为 true,析构后槽不被调用。

尝试在窗口关闭时做异步清理:

void SubWidget::closeEvent(QCloseEvent* e)
{QMainWindow::closeEvent(e);createWatcher();          // 启动 QtConcurrent::run
}

运行结果:

  • 控制台只打印
  Running in a separate threadSubWidget::~SubWidget: destructor called
  • 永远收不到
  onQFutureWatcherFinished ...

根本原因:

  1. createWatcher() 产生的 QFutureWatcher 没有父对象,也没有任何智能指针托管。
  2. closeEvent 返回后,事件循环继续,SubWidget 可能立即被 deleteLater 析构。
  3. 如果 SubWidget 先析构,而线程 3 秒后才发射 finished,信号会投递到一块已释放的内存。
  4. SubWidget 析构后,所有以 this 为接收者的 connect 自动断开;finished 信号再也找不到对象,于是消失在空气中。

2.方案总览

方案思路是否阻塞 UI是否安全适用场景
方案一:阻塞等待closeEventwaitForFinished()安全(但卡 UI)非常简单的清理工作,用户可接受假死
方案二:异步自毁QFutureWatcher 自己 deleteLater(),不再依赖 this安全真正异步,用户体验好

3.方案一:阻塞等待(waitForFinished)

3.1 代码

void SubWidget::closeEvent(QCloseEvent* e)
{QMainWindow::closeEvent(e);m_pWatcher = createWatcher();           // createWatcher 见题面if (m_pWatcher && m_pWatcher->isRunning()){qDebug() << "waiting...";m_pWatcher->waitForFinished();      // 阻塞qDebug() << "wait done";}
}

3.2 要点

  1. waitForFinished() 会阻塞当前线程(通常是 GUI 线程),界面会卡住 3 秒。
  2. 因为阻塞期间 SubWidget 对象仍在,所以槽函数可以正常调用,不会出现野指针。
  3. 不需要额外的 deleteLater(),函数结束后 m_pWatcher 作为普通局部变量被销毁即可。

3.3 优缺点

  • ✅ 实现简单、无生命周期坑
  • ❌ UI 假死;如果任务很长,体验极差

4.方案二:异步自毁(Lambda + deleteLater)

4.1 核心思想

  • QFutureWatcher 的生命周期与 SubWidget 解耦。
  • 用捕获列表 [=]pWatcher 拷进 lambda,不再使用 this
  • 任务结束时自己 deleteLater(),彻底避免野指针。

4.2 代码(简化后)

QFutureWatcher<bool>* SubWidget::createWatcher()
{auto* watcher = new QFutureWatcher<bool>;   // 无父对象watcher->setObjectName("watcher_async");// 关键:不连接到 this,而是连接到 lambdaconnect(watcher, &QFutureWatcher<bool>::finished,[watcher] {qDebug() << "finished, result =" << watcher->result();watcher->deleteLater();});watcher->setFuture(QtConcurrent::run([] {qDebug() << "Running in a separate thread";QThread::sleep(3);return true;}));return watcher;   // 调用者可立即返回,无需等待
}

调用方:

void SubWidget::closeEvent(QCloseEvent* e)
{QMainWindow::closeEvent(e);createWatcher();   // 立即返回,不阻塞
}

4.3 要点

  1. 不依赖 this:即使 SubWidget 马上析构,lambda 仍持有 watcher 的拷贝,不会变成悬垂连接。
  2. 自动销毁:deleteLater() 把销毁动作排入事件循环,线程结束后回到 GUI 线程时安全析构。
  3. 无内存泄漏:只要 finished 信号被发射一次,就必然触发 deleteLater()
  4. UI 不卡顿:完全符合“异步任务”初衷。

  1. 两种方案对比总结
维度方案一:阻塞等待方案二:异步自毁
是否卡 UI
代码复杂度
生命周期安全高(需遵循不访问 this
适用场景快速退出、简单清理耗时任务、优雅关闭
Qt 官方推荐

  1. 常见踩坑清单

  2. 在析构函数里 new 一个对象却不 delete

    → 内存泄漏或野指针。

  3. finished 连接到 this 槽,但对象先走

    → 信号投递到僵尸对象。

  4. destroyed 信号里再 deleteLater(sender())

    → 重复释放,直接崩溃。

  5. 以为 createWatcher() 返回的裸指针会自动管理

    → Qt 只有 QObject 树才会自动析构,普通裸指针不会。


  1. 一句话结论
  • 短任务、可接受假死 → 用方案一 waitForFinished(),简单粗暴。
  • 长任务、要求流畅 → 用方案二 Lambda + deleteLater,彻底摆脱对象生命周期噩梦。
http://www.dtcms.com/a/304094.html

相关文章:

  • 小白学OpenCV系列1-图像处理基本操作
  • Vue2 vs Vue3:核心差异与升级亮点
  • Django自带的加密算法
  • .NET Core 3.1 升级到 .NET 8
  • 如何编写部署和支持需求---SRS软件需求规格指南系列
  • .NET 10 中的新增功能系列文章1——运行时中的新增功能
  • C++跨平台连接多种数据库实战
  • 冒泡排序算法
  • Chrome 提示 “此扩展程序不再受支持”(MacOS/Windows)
  • CVPR 2025 | 华科精测:无需人工标注也能精准识别缺陷类别,AnomalyNCD 实现多类别缺陷自主分类
  • 实用PDF演示解决方案
  • Transformer实战——BERT模型详解与实现
  • 鸿蒙网络编程系列59-仓颉版TLS回声服务器示例
  • deepseek+飞书多维表格 打造小红书矩阵
  • [源力觉醒 创作者计划]_文心大模型4.5开源:从技术突破到生态共建的国产AI解读与本地部署指南
  • AI药师助手 + 药品图谱系统完整操作分析(python版)
  • 【数据可视化-74】电信用户流失数据可视化分析:Python + Pyecharts 炫酷大屏(含完整的数据,代码)
  • 【安装软件系列】Ubuntu安装MongoDb
  • 姑苏区人工智能大模型基础设施|参数1-3:服务器整体
  • React--》规划React组件库编码规范与标准 — Button篇
  • 第十三讲:C++多态
  • 多个参数组合生成sql的方法
  • BERT 的 NSP慢慢转换为SOP
  • arm v3 smmu 图示
  • Android四大组件之BroadcastReceiver解析
  • [2025CVPR]HUGNet2架构:事件相机光流估计
  • 智能落地扇方案:青稞RISC-V电机 MCU一览
  • Java 10 新特性解析
  • 【嵌入式电机控制#18】有刷直流串级控制
  • Redis的持久化策略-AOF和RDB(详细图解)