在qt中经常会遇到执行一个任务稍微有点耗时,放在主线程中会使界面无响应,但是创建一个类,再把类放到多线程中,又觉得费事,只是想调用一个函数而已,这时你需要用到qt的QtConcurrent,一个非常好用的后台线程执行类函数,下面介绍使用方法。
1.整体用法如下:QtConcurrent::run在后台执行,QMetaObject::invokeMethod回到主线程,这是Qt多线程编程的标准模式;
cpp
QtConcurrent::run([=]() {
// ✅ 这里是在后台线程执行(非UI线程)
// 可以执行耗时操作,不会阻塞界面
// 执行完成后...
QMetaObject::invokeMethod(this, [=]() {
// ✅ 这里回到主线程(UI线程)
// 可以安全地更新界面
});
});
- QtConcurrent::run - 后台线程,这里能写和不能写的内容如下:
cpp
QtConcurrent::run([=]() {
qDebug() << "当前线程:" << QThread::currentThread();
// 输出:可能是 QThreadPoolThread (不是主线程)
// 这里可以:
// - 执行耗时计算
// - 运行外部程序
// - 读写大文件
// - 网络请求
// 这里不能:
// - 直接更新UI组件 ❌
// - 直接访问UI对象 ❌
});
其实很好理解,只有主线程能更新界面,这里的 lambda函数已经不是主线程了,所以绝对不能把更新界面的内容写进去。
- 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() << "任务已提交到后台线程";
}