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() << "任务已提交到后台线程";
}
相关推荐
专注VB编程开发20年16 分钟前
AI 生成C# WinForm 窗体 = 目前就是垃圾
开发语言·人工智能·c#
cfm_291416 分钟前
JVM GC垃圾回收初步了解
java·开发语言·jvm
~小先生~27 分钟前
Python从入门到放弃(一)
开发语言·python
许彰午1 小时前
17_synchronized关键字深度解析
java·开发语言
z落落1 小时前
C# 泛型接口和泛型类+泛型约束
开发语言·c#
阿正的梦工坊1 小时前
【Rust】02-变量、不可变性与基础类型
开发语言·后端·rust
阿正的梦工坊1 小时前
【Rust】08-集合类型、字符串与迭代器入门
开发语言·rust·c#
FuckPatience1 小时前
C# 使用泛型协变将派生类类型替换为基类类型
开发语言·c#
张忠琳1 小时前
【Go 1.26.4】(Part 1) Go 1.26.4 超深度源码分析 — 总体架构与模块全景
开发语言·golang
guygg882 小时前
C# 生成中间带 Logo 头像的二维码
开发语言·c#