Qt简单任务的多线程操作(无需创建类)

在qt中经常会遇到执行一个任务稍微有点耗时,放在主线程中会使界面无响应,但是创建一个类,再把类放到多线程中,又觉得费事,只是想调用一个函数而已,这时你需要用到qt的QtConcurrent,一个非常好用的后台线程执行类函数,下面介绍使用方法。

1.整体用法如下:QtConcurrent::run在后台执行,QMetaObject::invokeMethod回到主线程,这是Qt多线程编程的标准模式;

cpp 复制代码
QtConcurrent::run([=]() {
    // ✅ 这里是在后台线程执行(非UI线程)
    // 可以执行耗时操作,不会阻塞界面
    

    // 执行完成后...
    QMetaObject::invokeMethod(this, [=]() {
        // ✅ 这里回到主线程(UI线程)
        // 可以安全地更新界面
    });
});
  1. QtConcurrent::run - 后台线程,这里能写和不能写的内容如下:
cpp 复制代码
QtConcurrent::run([=]() {
    qDebug() << "当前线程:" << QThread::currentThread();
    // 输出:可能是 QThreadPoolThread (不是主线程)
    
    // 这里可以:
    // - 执行耗时计算
    // - 运行外部程序
    // - 读写大文件
    // - 网络请求
    
    // 这里不能:
    // - 直接更新UI组件 ❌
    // - 直接访问UI对象 ❌
});

其实很好理解,只有主线程能更新界面,这里的 lambda函数已经不是主线程了,所以绝对不能把更新界面的内容写进去。

  1. QMetaObject::invokeMethod - 回到主线程,这里就可以更新界面了,具体写法有两种:

方式1:使用 lambda

cpp 复制代码
QMetaObject::invokeMethod(this, [=]() {
    qDebug() << "回到主线程:" << QThread::currentThread();
    // 输出:main thread (UI线程)
    
    // 这里可以:
    // - 更新文本框 ✅ ui->textEdit->setText()
    // - 修改按钮状态 ✅ ui->button->setEnabled()
    // - 显示对话框 ✅ QMessageBox::information()
});

方式2:调用成员函数

cpp 复制代码
QMetaObject::invokeMethod(this, "updateUI", 
    Qt::QueuedConnection, 
    Q_ARG(QString, result));

//对应的槽函数:
void MainWindow::updateUI(const QString& result) {
    // 这个函数在主线程执行
    ui->outputTextEdit->setText(result);
}

QtConcurrent实现了在一个函数里可以在其他线程和主线程之间来回切换,灵活调用你想执行的代码。

还没看懂怎么用吗?那再附上一个小例子:

cpp 复制代码
void MainWindow::runExternalProgram() {
    // 显示"开始"状态(主线程)
    ui->statusLabel->setText("开始执行...");
    ui->progressBar->setValue(0);
    
    // 启动后台任务
    QtConcurrent::run([this]() {
        // ========== 后台线程开始 ==========
        QProcess process;
        QString appImage = getAppImagePath();
        QString config = getConfigPath();
        
        // 耗时操作:启动外部程序并等待
        process.start(appImage, QStringList() << "librelane" << config);
        process.waitForFinished(30000); // 等待30秒
        
        // 获取结果
        QString output = process.readAllStandardOutput();
        int exitCode = process.exitCode();
        
        // 回到主线程更新UI
        QMetaObject::invokeMethod(this, [this, output, exitCode]() {
            // ========== 回到主线程 ==========
            if (exitCode == 0) {
                ui->statusLabel->setText("执行成功");
                ui->outputTextEdit->setPlainText(output);
                
                // 执行后续操作
                processResults(output);
            } else {
                ui->statusLabel->setText("执行失败");
                QString error = process.readAllStandardError();
                ui->outputTextEdit->setPlainText("错误:" + error);
            }
            
            ui->progressBar->setValue(100);
        });
        // ========== 后台线程结束 ==========
    });
    
    // 这里立即返回,界面不会卡住
    qDebug() << "任务已提交到后台线程";
}
相关推荐
小羊没烦恼!21 小时前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
伞伞悦读1 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
C语言小火车1 天前
C/C++ 为什么需要编译器?
开发语言·c++
霍霍的袁1 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
孙启超1 天前
【AI开发之Rust】第 11 课:智能指针与内部可变性
开发语言·后端·rust
此生决int1 天前
深入理解C++系列(20)——C++11(下)
开发语言·c++
慧都小项1 天前
当边缘AI上产线:QtitanDocking 如何让工业 HMI 实现多视图协同
人工智能·qt·ui·边缘计算·qt6.3
CoderYanger1 天前
A.每日一题:835. 图像重叠
java·开发语言·程序人生·leetcode·面试·职场和发展·学习方法
伞伞悦读1 天前
【第37期】Python JSON 与配置详解:序列化、反序列化、嵌套结构和配置文件
开发语言·python·json
CCCCCCCCharlie1 天前
Linux进程控制四大核心操作
linux·开发语言