采用官方推薦的 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 多線程的核心機制,包括線程創建、任務執行、跨線程通信和安全退出。