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 小时前
【爬虫教程】第7章:现代浏览器渲染引擎原理(Chromium/V8)
java·开发语言·爬虫
亮子AI21 小时前
【Python】比较两个cli库:Click vs Typer
开发语言·python
月明长歌21 小时前
Java进程与线程的区别以及线程状态总结
java·开发语言
qq_4017004121 小时前
QT C++ 好看的连击动画组件
开发语言·c++·qt
t1987512821 小时前
广义预测控制(GPC)实现滞后系统控制 - MATLAB程序
开发语言·matlab
报错小能手1 天前
线程池学习(六)实现工作窃取线程池(WorkStealingThreadPool)
开发语言·学习
一条咸鱼_SaltyFish1 天前
[Day10] contract-management初期开发避坑指南:合同模块 DDD 架构规划的教训与调整
开发语言·经验分享·微服务·架构·bug·开源软件·ai编程
额呃呃1 天前
STL内存分配器
开发语言·c++
七点半7701 天前
c++基本内容
开发语言·c++·算法