为了更高效的处理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
的方式来做异步解析。只要别二次使用一个已经被销毁的对象,就不会再崩溃了。