关于那个卡了我两天半的线程池
本文最后更新于21 天前,其中的信息可能已经过时,如有错误请留言评论。

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

    这是孩子学习qt的第72天
    暂无评论

    发送评论 编辑评论

    
    				
    |´・ω・)ノ
    ヾ(≧∇≦*)ゝ
    (☆ω☆)
    (╯‵□′)╯︵┴─┴
     ̄﹃ ̄
    (/ω\)
    ∠( ᐛ 」∠)_
    (๑•̀ㅁ•́ฅ)
    →_→
    ୧(๑•̀⌄•́๑)૭
    ٩(ˊᗜˋ*)و
    (ノ°ο°)ノ
    (´இ皿இ`)
    ⌇●﹏●⌇
    (ฅ´ω`ฅ)
    (╯°A°)╯︵○○○
    φ( ̄∇ ̄o)
    ヾ(´・ ・`。)ノ"
    ( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
    (ó﹏ò。)
    Σ(っ °Д °;)っ
    ( ,,´・ω・)ノ"(´っω・`。)
    ╮(╯▽╰)╭
    o(*////▽////*)q
    >﹏<
    ( ๑´•ω•) "(ㆆᴗㆆ)
    😂
    😀
    😅
    😊
    🙂
    🙃
    😌
    😍
    😘
    😜
    😝
    😏
    😒
    🙄
    😳
    😡
    😔
    😫
    😱
    😭
    💩
    👻
    🙌
    🖕
    👍
    👫
    👬
    👭
    🌚
    🌝
    🙈
    💊
    😶
    🙏
    🍦
    🍉
    😣
    Source: github.com/k4yt3x/flowerhd
    颜文字
    Emoji
    小恐龙
    花!
    上一篇
    下一篇