Qt 退出崩溃别只怪 delete,线程和对象释放顺序才是重灾区
Qt 项目里有一种 bug 很讨厌程序跑的时候一切正常采集、通信、界面刷新都没问题用户点右上角关闭按钮啪崩了。更尴尬的是开发机上不一定复现。现场工控机跑了两天关机前点一下退出直接弹崩溃报告。最后查下来问题不在算法也不在界面而是线程还没停干净对象已经开始释放了。为什么 demo 没问题项目里就开始炸因为 demo 里线程简单生命周期短退出路径也干净。真实项目不是这样。一个工业上位机里可能有串口采集线程、TCP 心跳线程、数据库写入线程、日志落盘线程UI 还在接收它们的信号。程序退出时如果 QWidget 已经析构了后台线程还 emit 信号或者 Worker 里还拿着某个已经释放的配置对象指针崩溃就很自然。Qt 对象树不是退出安全的万能药。parent 能帮你释放对象但它不负责帮你判断线程有没有结束更不保证业务对象的销毁顺序一定符合你的预期。项目里我一般会把退出看成一个独立流程而不是顺手 close 一下窗口。比如线程对象和 Worker 对象尽量明确绑定释放关系connect(worker,Worker::finished,worker,QObject::deleteLater);connect(thread,QThread::finished,thread,QObject::deleteLater);这两行看着普通但在项目里很关键。它解决的不是“少写 delete”这么简单而是让对象在合适的事件循环里释放。跨线程对象最怕的就是创建在一个线程释放在另一个线程平时没事退出时随机炸。真正麻烦的不是启动线程而是停线程。很多人写thread-quit();thread-wait();这只能说明你通知线程退出了不代表 Worker 当前业务已经安全收尾。比如串口正在读数据库事务还没提交socket 正在回调。更稳的做法是给 Worker 一个明确的 stop 接口让它自己停业务再发 finished。connect(qApp,QCoreApplication::aboutToQuit,worker,Worker::stop);这段代码解决的是退出时机问题。不要等主窗口析构到一半了才想起来通知线程停。退出信号要早发资源释放要晚做。常见坑第一个坑是在主窗口析构函数里直接 delete 线程相关对象。窗口析构时UI 子对象已经在释放后台线程如果还在发界面更新信号等于往废墟里投递消息。第二个坑是把 Worker 设置 parent 为主窗口。看起来对象树很整齐实际很危险。Worker 如果 moveToThread 之后生命周期就不该再被 UI 直接粗暴接管。线程归线程界面归界面别用 parent 掩盖设计混乱。第三个坑是依赖 disconnect 解决所有问题。disconnect 能断信号但断不了正在执行的槽函数也断不了 Worker 内部持有的裸指针。项目大了以后连接关系不清楚本身就是隐形债务。第四个坑是忽略日志线程、数据库线程这种“边角料”。很多程序退出崩溃不是采集线程炸的而是最后一条日志还没写完日志对象先被释放了。我自己的判断是Qt 程序退出崩溃大多不是某一行 delete 写错而是生命周期设计从一开始就没画边界。哪些对象属于 UI哪些对象属于线程谁负责通知停止谁负责最终释放这些必须明确。一个成熟的 Qt 项目退出流程应该像开机流程一样被认真设计。先停业务再停线程先断外设和网络再释放对象先让 Worker 自己收尾再让 QObject 树清场。