在 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: 适用于大量短任务的场景,避免了频繁创建和销毁线程的开销。
- 跨线程通信: 使用信号和槽进行线程间的通信,确保线程安全。