You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt 5.15.2中QtConcurrent::mapped致界面阻塞的原因及解决办法

相关代码

main.cpp

#include <QApplication>
#include <QtConcurrent/QtConcurrent>

int numTest(const int &num)
{
    QThread::msleep(5000);

    qDebug() << "numTest in thread" << QThread::currentThread();
    return num;
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QtConcurrent::run([](){
        const int count = 2000;

        QList<int> nums;
        for (int i = 0; i < count; ++i)
            nums.append(i);

        auto result = QtConcurrent::mapped(nums, numTest);
        result.waitForFinished();
    });

    BmMessageBox::information(nullptr, "tips", "There is no help document temporarily!");

    return app.exec();
}

bmmessagebox.h

#ifndef BMMESSAGEBOX_H
#define BMMESSAGEBOX_H

#include <QDialog>
#include <QIcon>

class BmMessageBox : QObject
{
    Q_OBJECT

public:
    static void information(QWidget *parent, const QString &title, const QString &text) {
        Q_UNUSED(title);
        Q_UNUSED(text);

        QDialog box(parent);
        box.setWindowIcon(QIcon(":/resources/images/test.png"));
        box.exec();
    }
};

#endif // BMMESSAGEBOX_H

resources.qrc

<RCC>
    <qresource prefix="/">
        <file>resources/images/test.png</file>
    </qresource>
</RCC>

问题描述

在Qt 5.15.2版本中,使用QtConcurrent::mapped时,调用BmMessageBox::information设置资源文件中的窗口图标会导致界面阻塞,但该问题在Qt 5.12.12版本中并未出现。请问该问题的成因是什么?如何在不更换Qt版本的前提下解决此问题?

问题成因

Qt 5.15.x对QtConcurrent线程池和资源文件访问的线程安全性做了调整:

  1. QtConcurrent::mapped会启动大量工作线程,而Qt 5.15.2中QResource(资源文件)的访问新增了全局锁机制,当工作线程和主线程同时尝试访问资源时,会出现锁竞争。
  2. 主线程显示模态对话框box.exec()时会占用事件循环,同时QtConcurrent::run里的result.waitForFinished()会让启动任务的线程一直等待,双重叠加导致主线程被资源锁阻塞,最终界面卡死。

解决方案

方案1:提前预加载资源

在主线程启动QtConcurrent任务前,先加载一次需要的资源,将其缓存到内存中,避免后续模态对话框加载时触发锁竞争:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // 预加载资源到内存
    QIcon preloadIcon(":/resources/images/test.png");

    QtConcurrent::run([](){
        const int count = 2000;
        QList<int> nums;
        for (int i = 0; i < count; ++i)
            nums.append(i);

        auto result = QtConcurrent::mapped(nums, numTest);
        result.waitForFinished();
    });

    BmMessageBox::information(nullptr, "tips", "There is no help document temporarily!");

    return app.exec();
}

方案2:避免阻塞QtConcurrent启动线程

去掉result.waitForFinished(),让任务在后台异步执行,不占用启动线程的资源,减少锁竞争概率:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QtConcurrent::run([](){
        const int count = 2000;
        QList<int> nums;
        for (int i = 0; i < count; ++i)
            nums.append(i);

        // 直接启动任务,不等待完成
        QtConcurrent::mapped(nums, numTest);
    });

    BmMessageBox::information(nullptr, "tips", "There is no help document temporarily!");

    return app.exec();
}

如果需要监听任务完成状态,可使用QFutureWatcher绑定信号:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QFutureWatcher<int> watcher;
    QObject::connect(&watcher, &QFutureWatcher<int>::finished, [](){
        qDebug() << "所有任务执行完成";
    });

    QtConcurrent::run([&watcher](){
        const int count = 2000;
        QList<int> nums;
        for (int i = 0; i < count; ++i)
            nums.append(i);

        auto result = QtConcurrent::mapped(nums, numTest);
        watcher.setFuture(result);
    });

    BmMessageBox::information(nullptr, "tips", "There is no help document temporarily!");

    return app.exec();
}

方案3:绕过资源文件锁

将图标文件作为独立本地文件存放,而非嵌入到资源中,直接访问本地文件避免QResource的全局锁:

// 修改BmMessageBox::information中的图标加载代码
box.setWindowIcon(QIcon("resources/images/test.png"));

需确保程序运行时该路径下存在对应的图片文件。

内容的提问来源于stack exchange,提问作者bmseven

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 01:07:43