Qt 中线程使用

在 Qt 中,线程的管理和使用非常重要,特别是在需要并发处理和避免界面卡顿的情况下。Qt 提供了几种方式来创建和管理线程,最常用的有 QThread 类和通过继承 QRunnable 的线程池。下面我将分别介绍这几种方式。

1. QThread 基本用法

QThread 是 Qt 提供的线程管理类,可以通过它创建和管理独立的线程。您可以将任务移到线程中执行,而不会阻塞主线程。

1.1. 继承 QThread 创建线程

您可以通过继承 QThread 类并重写 run() 方法来创建线程。

cpp 复制代码
#include <QCoreApplication>
#include <QThread>
#include <QDebug>

// 创建一个继承自 QThread 的类
class WorkerThread : public QThread {
    Q_OBJECT
public:
    WorkerThread() {}

protected:
    void run() override {
        // 这里是线程执行的任务
        for (int i = 0; i < 5; ++i) {
            qDebug() << "Worker thread is running, iteration:" << i;
            QThread::sleep(1);  // 模拟耗时操作
        }
        emit finished();  // 发出 finished 信号,表示线程完成任务
    }

signals:
    void finished();  // 线程完成信号
};

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    WorkerThread workerThread;
    QObject::connect(&workerThread, &WorkerThread::finished, &a, &QCoreApplication::quit);
    workerThread.start();  // 启动线程

    return a.exec();
}

1.2. 使用 moveToThread()

如果你不想继承 QThread,可以通过 moveToThread() 将一个对象移到一个新的线程中执行。

cpp 复制代码
#include <QCoreApplication>
#include <QThread>
#include <QObject>
#include <QDebug>

class Worker : public QObject {
    Q_OBJECT
public:
    Worker() {}

public slots:
    void doWork() {
        qDebug() << "Worker thread is running!";
        QThread::sleep(2);  // 模拟耗时操作
        emit finished();
    }

signals:
    void finished();
};

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    Worker worker;
    QThread thread;

    // 将 worker 移到新的线程中
    worker.moveToThread(&thread);

    // 当线程启动时,调用 worker 的 doWork 方法
    QObject::connect(&thread, &QThread::started, &worker, &Worker::doWork);

    // 线程完成后退出
    QObject::connect(&worker, &Worker::finished, &a, &QCoreApplication::quit);

    thread.start();  // 启动线程

    return a.exec();
}

2. 使用线程池 (QRunnable + QThreadPool)

对于大量短时间运行的任务,线程池是一个更高效的选择,避免了频繁创建和销毁线程的开销。QThreadPool 类可以管理和调度 QRunnable 对象。

2.1. 使用 QRunnable

QRunnable 允许将任务提交到线程池执行,而不需要直接创建 QThread 对象。

cpp 复制代码
#include <QCoreApplication>
#include <QThreadPool>
#include <QRunnable>
#include <QDebug>

class Task : public QRunnable {
public:
    void run() override {
        // 任务逻辑
        qDebug() << "Task is running in thread:" << QThread::currentThread();
        QThread::sleep(1);  // 模拟任务执行
    }
};

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    // 获取默认线程池
    QThreadPool *pool = QThreadPool::globalInstance();

    // 创建并提交任务
    for (int i = 0; i < 5; ++i) {
        Task *task = new Task();
        pool->start(task);  // 启动任务
    }

    return a.exec();
}

2.2. 线程池的参数设置

QThreadPool 允许你设置最大线程数、优先级等参数:

cpp 复制代码
QThreadPool *pool = QThreadPool::globalInstance();
pool->setMaxThreadCount(10);  // 设置最大线程数
pool->start(task);  // 启动任务

3. 线程间通信

在 Qt 中,线程之间的通信通常使用信号和槽。由于 Qt 的事件循环机制,线程间的信号和槽可以安全地跨线程传递数据。

3.1. 跨线程信号和槽

在 Qt 中,线程间的信号和槽是异步的。默认情况下,Qt 会自动将信号从一个线程排队到另一个线程。通过 Qt::QueuedConnection 连接类型,可以保证线程间信号的安全传递。

cpp 复制代码
// 假设 worker 运行在一个线程中
connect(worker, &Worker::finished, this, &MainWindow::onWorkerFinished, Qt::QueuedConnection);

4. 注意事项

  • 线程安全: 在多线程编程中,必须保证对共享数据的访问是线程安全的。可以使用 QMutex、QMutexLocker 来避免数据竞争。
  • 避免 UI 阻塞: 在 GUI 应用程序中,耗时操作必须在子线程中执行,避免阻塞主线程的事件循环,确保 UI 不会冻结。
  • 线程终止: 通常不要直接调用 QThread::terminate() 来强行结束线程。可以通过设计良好的线程控制信号来安全地终止线程。

5. 总结

  • QThread: 适用于需要执行耗时操作并与界面进行交互的场景。
  • QRunnable + QThreadPool: 适用于大量短任务的场景,避免了频繁创建和销毁线程的开销。
  • 跨线程通信: 使用信号和槽进行线程间的通信,确保线程安全。
相关推荐
用户805533698034 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner4 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz9 天前
QML Hello World 入门示例
qt
xcyxiner12 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner13 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00616 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术16 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript