QT中多线程的实现
采用官方推荐的 QObject::moveToThread
方式实现(相比继承 QThread
更灵活),包含耗时任务执行、主线程通信、线程安全退出等核心功能。
环境说明
- Qt 版本:Qt 5.15+ 或 Qt 6(兼容)
- 项目类型:GUI 程序(含界面显示线程状态和结果)
- 依赖模块:需在
.pro
文件中添加QT += core gui widgets
完整代码(含注释)
1. 项目文件(.pro)
QT += core gui widgets
CONFIG += c++11TARGET = QtMultiThreadDemo
TEMPLATE = appSOURCES += main.cpp \mainwindow.cppHEADERS += \mainwindow.h \worker.hFORMS += 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_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic: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 多线程的核心机制,包括线程创建、任务执行、跨线程通信和安全退出。