qt中qthread详解

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();
}

总结

  1. 推荐使用 moveToThread 模式,而不是继承 QThread

  2. GUI 操作必须在主线程中进行

  3. 使用信号槽进行线程间通信,这是 Qt 最安全的方式

  4. 注意对象的生命周期,使用 deleteLater 安全删除

  5. 合理使用线程同步原语,避免竞争条件

  6. 考虑使用线程池处理大量短期任务

相关推荐
xcyxiner11 小时前
DicomViewer (后台线程处理文件)4
qt
xcyxiner18 小时前
DicomViewer (添加模型类)3
qt
xcyxiner2 天前
DicomViewer (目录调整) 2
qt
xcyxiner2 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
clint4563 天前
C++进阶(1)——前景提要
c++
夜悊3 天前
C++代码示例:进制数简单生成工具
c++
郝学胜_神的一滴3 天前
CMake 021: IF 条件判据详诠
c++·cmake
_wyt0013 天前
洛谷 B3930 [GESP202312 五级] 烹饪问题 题解
c++·gesp
玖玥拾3 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
桥田智能4 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构