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. 考虑使用线程池处理大量短期任务

相关推荐
寻寻觅觅☆8 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
fpcc8 小时前
并行编程实战——CUDA编程的Parallel Task类型
c++·cuda
ceclar1239 小时前
C++使用format
开发语言·c++·算法
lanhuazui1010 小时前
C++ 中什么时候用::(作用域解析运算符)
c++
charlee4410 小时前
从零实现一个生产级 RAG 语义搜索系统:C++ + ONNX + FAISS 实战
c++·faiss·onnx·rag·语义搜索
老约家的可汗10 小时前
初识C++
开发语言·c++
crescent_悦10 小时前
C++:Product of Polynomials
开发语言·c++
小坏坏的大世界11 小时前
CMakeList.txt模板与 Visual Studio IDE 操作对比表
c++·visual studio
乐观勇敢坚强的老彭11 小时前
c++寒假营day03
java·开发语言·c++
郑同学zxc11 小时前
机器视觉10-Qt联合Halcon开发环境配置
开发语言·qt