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() << "任务已提交到后台线程";
}
相关推荐
froginwe114 分钟前
Python3与MySQL的连接:使用mysql-connector
开发语言
杜子不疼.24 分钟前
PyPTO:面向NPU的高效并行张量编程范式
开发语言
lly20240625 分钟前
C# 结构体(Struct)
开发语言
YMWM_35 分钟前
python3继承使用
开发语言·python
Once_day1 小时前
C++之《程序员自我修养》读书总结(1)
c语言·开发语言·c++·程序员自我修养
xmRao1 小时前
Qt+FFmpeg 实现 PCM 音频转 AAC 编码
qt·ffmpeg·pcm
xmRao1 小时前
Qt+FFmpeg 实现录音程序(pcm转wav)
qt·ffmpeg
喜欢喝果茶.1 小时前
QOverload<参数列表>::of(&函数名)信号槽
开发语言·qt
亓才孓1 小时前
[Class类的应用]反射的理解
开发语言·python