Qt QThread 详解
1. QThread 概述
QThread 是 Qt 中用于多线程编程的核心类。它提供了一种平台无关的方式来管理线程。
2. 两种使用方式
方式一:继承 QThread(传统方法)
cpp
// 继承 QThread 并重写 run() 方法
class WorkerThread : public QThread
{
Q_OBJECT
protected:
void run() override {
// 这里运行在新线程中
for(int i = 0; i < 5; i++) {
qDebug() << "Working in thread" << QThread::currentThreadId();
sleep(1);
}
}
};
// 使用
WorkerThread thread;
thread.start(); // 调用 start() 会执行 run()
thread.wait(); // 等待线程结束
方式二:使用 Worker 对象 + moveToThread(推荐方法)
cpp
// Worker 类:包含实际工作的对象
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork() {
// 耗时操作
for(int i = 0; i < 5; i++) {
emit progress(i * 20);
QThread::sleep(1);
}
emit finished();
}
signals:
void progress(int value);
void finished();
};
// 在 GUI 线程中使用
class Controller : public QObject
{
Q_OBJECT
public:
Controller() {
worker = new Worker();
thread = new QThread();
// 将 worker 移动到新线程
worker->moveToThread(thread);
// 连接信号槽
connect(thread, &QThread::started, worker, &Worker::doWork);
connect(worker, &Worker::finished, thread, &QThread::quit);
connect(worker, &Worker::finished, worker, &Worker::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
}
~Controller() {
if (thread->isRunning()) {
thread->quit();
thread->wait();
}
}
void start() {
thread->start();
}
private:
QThread* thread;
Worker* worker;
};
3. 线程生命周期管理
cpp
// 1. 创建和启动
QThread* thread = new QThread();
thread->start();
// 2. 检查线程状态
if (thread->isRunning()) {
qDebug() << "Thread is running";
}
if (thread->isFinished()) {
qDebug() << "Thread has finished";
}
// 3. 优雅终止
thread->quit(); // 发送退出请求
thread->wait(); // 等待线程实际退出
// 4. 强制终止(不推荐)
// thread->terminate(); // 危险!可能导致资源泄漏
// 5. 线程优先级
thread->setPriority(QThread::HighPriority);
4. 线程间通信
信号槽机制(自动排队)
cpp
// 跨线程的信号槽连接是队列连接(QueuedConnection)
// Qt 会自动处理线程间的消息传递
connect(sender, &Sender::signalName,
receiver, &Receiver::slotName,
Qt::QueuedConnection); // 可省略,Qt 会自动判断
使用 QMetaObject::invokeMethod
cpp
// 安全地在另一个线程调用方法
QMetaObject::invokeMethod(receiver, "slotName",
Qt::QueuedConnection,
Q_ARG(QString, "参数1"),
Q_ARG(int, 123));
5. 线程同步
QMutex(互斥锁)
cpp
class ThreadSafeCounter
{
public:
void increment() {
mutex.lock();
count++;
mutex.unlock();
}
int value() const {
QMutexLocker locker(&mutex); // RAII 风格,自动解锁
return count;
}
private:
mutable QMutex mutex;
int count = 0;
};
QReadWriteLock(读写锁)
cpp
class ThreadSafeData
{
public:
void writeData(const QString& data) {
QWriteLocker locker(&lock);
this->data = data;
}
QString readData() const {
QReadLocker locker(&lock);
return data;
}
private:
mutable QReadWriteLock lock;
QString data;
};
QWaitCondition(条件变量)
cpp
class Buffer
{
public:
void put(const QByteArray& data) {
QMutexLocker locker(&mutex);
while (buffer.size() > maxSize) {
notFull.wait(&mutex);
}
buffer.enqueue(data);
notEmpty.wakeAll();
}
QByteArray get() {
QMutexLocker locker(&mutex);
while (buffer.isEmpty()) {
notEmpty.wait(&mutex);
}
QByteArray data = buffer.dequeue();
notFull.wakeAll();
return data;
}
private:
QMutex mutex;
QWaitCondition notFull;
QWaitCondition notEmpty;
QQueue<QByteArray> buffer;
int maxSize = 100;
};
6. 线程池(QThreadPool + QRunnable)
cpp
class Task : public QRunnable
{
public:
Task(int id) : taskId(id) {}
void run() override {
qDebug() << "Task" << taskId << "running in thread"
<< QThread::currentThreadId();
QThread::sleep(1);
}
private:
int taskId;
};
// 使用线程池
for (int i = 0; i < 10; ++i) {
Task* task = new Task(i);
task->setAutoDelete(true); // 任务完成后自动删除
QThreadPool::globalInstance()->start(task);
}
// 等待所有任务完成
QThreadPool::globalInstance()->waitForDone();
7. 最佳实践和注意事项
7.1 使用 moveToThread 模式的优势
-
更符合 Qt 的事件驱动模型
-
可以复用 QThread 对象
-
更容易管理对象的生命周期
-
支持多个 worker 在同一个线程中运行
7.2 GUI 线程规则
cpp
// 永远不要在非 GUI 线程中进行 UI 操作
void Worker::doWork() {
// 错误:在非 GUI 线程更新 UI
// label->setText("Done"); // 可能导致崩溃!
// 正确:通过信号槽通知主线程更新 UI
emit updateUI("Done");
}
7.3 对象生命周期管理
cpp
// 正确的对象清理
connect(thread, &QThread::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
// 或者在父对象析构时
~Controller() {
thread->quit();
thread->wait();
delete worker;
delete thread;
}
7.4 事件循环
cpp
// 默认情况下,QThread 有自己的事件循环
// 可以在 run() 中手动启动事件循环
void run() override {
// 启动事件循环
exec();
// 或者手动处理事件
while (!isInterruptionRequested()) {
// 处理任务
QThread::msleep(100);
// 处理事件
QCoreApplication::processEvents();
}
}
8. 完整示例
cpp
// mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
private slots:
void onStartClicked();
void onProgress(int value);
void onFinished();
private:
QThread* workerThread;
Worker* worker;
QProgressBar* progressBar;
QPushButton* startButton;
};
// mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// 创建 UI 组件
progressBar = new QProgressBar(this);
startButton = new QPushButton("开始", this);
// 创建线程和 worker
workerThread = new QThread(this);
worker = new Worker();
worker->moveToThread(workerThread);
// 连接信号槽
connect(startButton, &QPushButton::clicked, this, &MainWindow::onStartClicked);
connect(worker, &Worker::progress, this, &MainWindow::onProgress);
connect(worker, &Worker::finished, this, &MainWindow::onFinished);
connect(worker, &Worker::finished, workerThread, &QThread::quit);
connect(workerThread, &QThread::finished, worker, &Worker::deleteLater);
// 布局
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(progressBar);
layout->addWidget(startButton);
QWidget* centralWidget = new QWidget(this);
centralWidget->setLayout(layout);
setCentralWidget(centralWidget);
}
void MainWindow::onStartClicked()
{
if (!workerThread->isRunning()) {
workerThread->start();
startButton->setEnabled(false);
}
}
void MainWindow::onProgress(int value)
{
progressBar->setValue(value);
}
void MainWindow::onFinished()
{
progressBar->setValue(100);
startButton->setEnabled(true);
workerThread->quit();
}
总结
-
推荐使用 moveToThread 模式,而不是继承 QThread
-
GUI 操作必须在主线程中进行
-
使用信号槽进行线程间通信,这是 Qt 最安全的方式
-
注意对象的生命周期,使用 deleteLater 安全删除
-
合理使用线程同步原语,避免竞争条件
-
考虑使用线程池处理大量短期任务