QT中多线程的实现

发布于:2025-05-11 ⋅ 阅读:(22) ⋅ 点赞:(0)

采用官方推荐的 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. 线程通信
  • 子线程→主线程:通过信号 progressUpdatedresultReady 发送进度和结果,Qt 会自动将信号排队到主线程执行(跨线程信号槽默认使用 Qt::QueuedConnection)。
  • 主线程→子线程:通过 QMetaObject::invokeMethod 调用子线程中的槽函数(startTask),确保在子线程上下文中执行。
3. 线程安全退出
  • 窗口关闭时,调用 workerThread->quit() 通知子线程退出事件循环,workerThread->wait() 等待线程完全停止,避免资源泄漏。
  • 子线程结束时,通过 connect(workerThread, &QThread::finished, worker, &QObject::deleteLater) 自动释放工作类实例。
4. 界面交互
  • 点击“启动任务”按钮后,按钮禁用(setEnabled(false)),防止重复触发。
  • 进度条(QProgressBar)实时显示任务进度,状态标签显示文字提示。

运行效果

  1. 编译运行程序,点击“启动任务”按钮。
  2. 进度条每秒更新一次(总耗时 5 秒),状态标签显示当前进度(如“进度:3/10”)。
  3. 任务完成后,状态标签显示“任务完成!总耗时:5秒”,按钮重新启用。

扩展说明

  • 耗时任务替代:可将 QThread::msleep(500) 替换为实际的耗时操作(如文件读写、网络请求、复杂计算)。
  • 多任务支持:若需执行多个独立任务,可在 Worker 类中添加多个槽函数(如 startTaskAstartTaskB),通过 invokeMethod 按需调用。
  • 线程池:若需管理多个线程,可使用 QThreadPoolQRunnable(适合短任务),但 moveToThread 更适合长时间运行的任务。

这个示例完整展示了 Qt 多线程的核心机制,包括线程创建、任务执行、跨线程通信和安全退出。


网站公告

今日签到

点亮在社区的每一天
去签到