采用官方推荐的 QObject::moveToThread
方式实现(相比继承 QThread
更灵活),包含耗时任务执行、主线程通信、线程安全退出等核心功能。
环境说明
- Qt 版本:Qt 5.15+ 或 Qt 6(兼容)
- 项目类型:GUI 程序(含界面显示线程状态和结果)
- 依赖模块:需在
.pro
文件中添加QT += core gui widgets
完整代码(含注释)
1. 项目文件(.pro)
QT += core gui widgets
CONFIG += c++11
TARGET = QtMultiThreadDemo
TEMPLATE = app
SOURCES += main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h \
worker.h
FORMS += mainwindow.ui
2. 工作类(worker.h)
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include <QThread>
#include <QDebug>
// 工作类:负责执行耗时任务(如数据计算、文件读写等)
class Worker : public QObject {
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr) : QObject(parent) {}
signals:
// 发送任务进度(参数:当前进度,总进度)
void progressUpdated(int current, int total);
// 发送任务结果(参数:结果字符串)
void resultReady(const QString &result);
public slots:
// 启动任务的槽函数(将在子线程中执行)
void startTask() {
const int totalSteps = 10;
for (int i = 0; i <= totalSteps; ++i) {
// 模拟耗时操作(如计算、延迟)
QThread::msleep(500); // 暂停 500ms
// 发送进度(跨线程信号,自动排队到主线程)
emit progressUpdated(i, totalSteps);
}
// 发送最终结果
emit resultReady("任务完成!总耗时:5秒");
}
};
3. 主窗口类(mainwindow.h)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QThread>
#include "worker.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
// 点击按钮启动任务
void on_startBtn_clicked();
// 接收进度更新的槽函数(主线程执行)
void onProgressUpdated(int current, int total);
// 接收任务结果的槽函数(主线程执行)
void onResultReady(const QString &result);
private:
Ui::MainWindow *ui;
QThread *workerThread; // 子线程对象
Worker *worker; // 工作类实例
};
#endif
4. 主窗口实现(mainwindow.cpp)
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow) {
ui->setupUi(this);
setWindowTitle("Qt 多线程示例");
// 初始化子线程和工作类
workerThread = new QThread(this); // 父对象设为窗口,自动管理生命周期
worker = new Worker(); // 工作类无父对象(后续移动到子线程)
// 将工作类移动到子线程
worker->moveToThread(workerThread);
// 连接信号槽(跨线程自动队列)
connect(ui->startBtn, &QPushButton::clicked, this, &MainWindow::on_startBtn_clicked);
connect(worker, &Worker::progressUpdated, this, &MainWindow::onProgressUpdated);
connect(worker, &Worker::resultReady, this, &MainWindow::onResultReady);
// 子线程结束时释放工作类(可选)
connect(workerThread, &QThread::finished, worker, &QObject::deleteLater);
}
MainWindow::~MainWindow() {
// 窗口关闭时,停止子线程并等待退出
workerThread->quit();
workerThread->wait(); // 等待线程结束(避免强制终止导致资源泄漏)
delete ui;
}
// 点击按钮启动任务
void MainWindow::on_startBtn_clicked() {
if (!workerThread->isRunning()) {
// 启动子线程(不直接执行任务,而是触发工作类的槽函数)
workerThread->start();
// 调用工作类的 startTask 槽函数(在子线程中执行)
QMetaObject::invokeMethod(worker, "startTask", Qt::QueuedConnection);
ui->startBtn->setEnabled(false); // 防止重复点击
}
}
// 更新进度(主线程执行)
void MainWindow::onProgressUpdated(int current, int total) {
ui->progressBar->setRange(0, total);
ui->progressBar->setValue(current);
ui->statusLabel->setText(QString("进度:%1/%2").arg(current).arg(total));
}
// 接收任务结果(主线程执行)
void MainWindow::onResultReady(const QString &result) {
ui->statusLabel->setText(result);
ui->startBtn->setEnabled(true); // 允许再次启动
workerThread->quit(); // 任务完成后停止子线程(可选)
}
5. 主函数(main.cpp)
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
6. 界面文件(mainwindow.ui)
通过 Qt Designer 设计界面,包含以下控件(可直接复制 XML 到 .ui
文件):
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>200</height>
</rect>
</property>
<property name="windowTitle">
<string>Qt 多线程示例</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>点击按钮启动任务</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="startBtn">
<property name="text">
<string>启动任务</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</ui>
代码核心逻辑说明
1. 多线程实现方式
采用 QObject::moveToThread
方式,将工作类 Worker
移动到子线程中执行任务,而非直接继承 QThread
并重写 run()
。这种方式的优势:
- 工作类通过信号槽与主线程通信,符合 Qt 事件驱动模型。
- 支持多个任务在同一个线程中顺序执行(通过队列槽函数调用)。
2. 线程通信
- 子线程→主线程:通过信号
progressUpdated
和resultReady
发送进度和结果,Qt 会自动将信号排队到主线程执行(跨线程信号槽默认使用Qt::QueuedConnection
)。 - 主线程→子线程:通过
QMetaObject::invokeMethod
调用子线程中的槽函数(startTask
),确保在子线程上下文中执行。
3. 线程安全退出
- 窗口关闭时,调用
workerThread->quit()
通知子线程退出事件循环,workerThread->wait()
等待线程完全停止,避免资源泄漏。 - 子线程结束时,通过
connect(workerThread, &QThread::finished, worker, &QObject::deleteLater)
自动释放工作类实例。
4. 界面交互
- 点击“启动任务”按钮后,按钮禁用(
setEnabled(false)
),防止重复触发。 - 进度条(
QProgressBar
)实时显示任务进度,状态标签显示文字提示。
运行效果
- 编译运行程序,点击“启动任务”按钮。
- 进度条每秒更新一次(总耗时 5 秒),状态标签显示当前进度(如“进度:3/10”)。
- 任务完成后,状态标签显示“任务完成!总耗时:5秒”,按钮重新启用。
扩展说明
- 耗时任务替代:可将
QThread::msleep(500)
替换为实际的耗时操作(如文件读写、网络请求、复杂计算)。 - 多任务支持:若需执行多个独立任务,可在
Worker
类中添加多个槽函数(如startTaskA
、startTaskB
),通过invokeMethod
按需调用。 - 线程池:若需管理多个线程,可使用
QThreadPool
和QRunnable
(适合短任务),但moveToThread
更适合长时间运行的任务。
这个示例完整展示了 Qt 多线程的核心机制,包括线程创建、任务执行、跨线程通信和安全退出。