为了更高效的处理hex文件与ui界面间的关系,将hex处理扔到了线程池中去操作,只是没想到这半吊子的水平处理不好线程池间的关系,导致卡住了两天。
为什么会卡住我两天
起因是在创建一个解析hex线程之后,将任务移到线程池。只是hex的解析并不是一次性,可能有多个hex需要加载,这就导致了问题的出现—多次加载hex都是把同一个对象多次提交给了线程池,还设置了线程池自动管理任务,这就导致线程池每次执行完任务后会自动销毁对象,再次提交任务对象时,this指向变成了野指针。如下面的代码:
void MainWindow::HexpressTaskThread()
{
// 创建解析器和线程
m_hexParser = new HexParser();
// 连接信号槽
connect(this, &MainWindow::sendhexdata, this, [this](const QByteArray &data) {
m_hexParser->setHexData(data);
QThreadPool::globalInstance()->start(m_hexParser);
});
connect(m_hexParser, &HexParser::parseFinished,
this, &MainWindow::onHexDataParsed, Qt::QueuedConnection);
connect(m_hexParser, &HexParser::parseError,
this, [this](const QString &err){
QMessageBox::warning(this, "Error", err);
}, Qt::QueuedConnection);
}
把同一个 HexParser 对象重复地提交给了 QThreadPool,而 QThreadPool 默认会在 run() 执行完后自动销毁(auto-delete)这个 QRunnable 对象。第二次再去调用 setHexData 时,this 其实已经是野指针,从而导致了进程崩溃,程序崩溃。
原因拆解
QThreadPool 默认的行为
HexParser 同时继承了 QObject 和 QRunnable(或至少实现了 QRunnable 的接口),并且没有显式关闭自动删除,那么在第一次调用QThreadPool::globalInstance()->start(m_hexParser);并且 run() 方法结束后,QThreadPool 会自动 delete this,导致原来的 HexParser 对象指针失效。
第二次加载文件时继续使用失效对象
在 HexpressTaskThread() 里:
connect(this, &MainWindow::sendhexdata, this, [this](const QByteArray &data) {
m_hexParser->setHexData(data);
QThreadPool::globalInstance()->start(m_hexParser);
});
当第二次加载文件再次触发 sendhexdata 信号后,就会对一个已经被销毁了的对象再去 setHexData,从而导致了进程崩溃。
如何修复
动态创建HexParser实例:每次接收到sendhexdata信号时,动态创建一个新的HexParser对象,而非复用同一个对象。
调整信号连接逻辑:将信号槽连接移动到动态对象创建处,确保每个实例独立管理。
void MainWindow::HexpressTaskThread()
{
// 移除预先创建的m_hexParser,改为在信号触发时动态创建
connect(this, &MainWindow::sendhexdata, this, [this](const QByteArray &data) {
// 每次创建新的HexParser实例
HexParser* hexParser = new HexParser();
hexParser->setHexData(data);
// 连接信号槽(需捕获hexParser指针)
connect(hexParser, &HexParser::parseFinished,
this, &MainWindow::onHexDataParsed, Qt::QueuedConnection);
connect(hexParser, &HexParser::parseError,
this, [this](const QString &err) {
QMessageBox::warning(this, "Error", err);
}, Qt::QueuedConnection);
// 提交任务到线程池
QThreadPool::globalInstance()->start(hexParser);
});
}
实际上也可以这样:设置 setAutoDelete(false) 并手动管理;或者改用传统 QThread + moveToThread 的方式来做异步解析。只要别二次使用一个已经被销毁的对象,就不会再崩溃了。





