C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > Qt 多线程QThread、QThreadPool、QConCurrent区别

Qt中多线程QThread、QThreadPool、QConCurrent三种方式对比分析

作者:追烽少年x

QThread是Qt多线程的基石,本质上是对操作系统原生线程的面向对象封装,本文介绍Qt中多线程QThread、QThreadPool、QConCurrent三种方式对比分析,感兴趣的朋友一起看看吧

第一层:QThread —— 基石与底层控制(管理“线程”)

QThread 是 Qt 多线程的基石,本质上是对操作系统原生线程的面向对象封装。

代码示例:

  1. QThread (moveToThread 法) —— 面向对象,事件循环驱动

这是 Qt 官方推荐的做法。我们将创建一个 Worker 类,将其移入 QThread,通过信号槽触发任务并返回结果。

核心特征:线程常驻、支持信号槽、拥有独立事件循环。

#include <QCoreApplication>
#include <QThread>
#include <QObject>
#include <QDebug>
// 1. 定义工作类(千万不要继承QThread)
class Worker : public QObject {
    Q_OBJECT
public:
    explicit Worker(int id) : m_id(id) {}
public slots:
    // 槽函数:执行耗时任务
    void doWork() {
        qDebug() << "Worker" << m_id << "running in thread:" << QThread::currentThreadId();
        QThread::msleep(1000); // 模拟耗时操作
        int result = m_id * 100; // 模拟计算结果
        emit resultReady(m_id, result); // 发送结果信号
    }
signals:
    void resultReady(int id, int result);
private:
    int m_id;
};
int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    qDebug() << "Main thread:" << QThread::currentThreadId();
    QList<QThread*> threads;
    QList<Worker*> workers;
    // 创建 5 个工作对象和 5 个线程
    for (int i = 0; i < 5; ++i) {
        Worker *worker = new Worker(i);
        QThread *thread = new QThread();
        worker->moveToThread(thread); // 将工作对象移入新线程
        // 连接启动信号:线程启动后,调用 worker 的 doWork
        QObject::connect(thread, &QThread::started, worker, &Worker::doWork);
        // 连接结果信号:将结果打印到主线程
        QObject::connect(worker, &Worker::resultReady, [](int id, int result){
            qDebug() << "  -> Got result from Worker" << id << ":" << result;
        });
        // 连接清理信号:任务完成后,安全退出线程并清理内存
        QObject::connect(worker, &Worker::resultReady, thread, &QThread::quit);
        QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater);
        QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater);
        threads.append(thread);
        workers.append(worker);
    }
    // 启动所有线程
    for (QThread *t : threads) {
        t->start();
    }
    return a.exec();
}

代码运行结果:

第二层:QThreadPool + QRunnable —— 任务池化(管理“任务”)

随着需求的复杂,你发现频繁创建销毁 QThread 太浪费资源,于是进阶到了线程池。QRunnable 是任务,QThreadPool 是执行任务的池子。

代码示例:

  1. QThreadPool + QRunnable —— 任务池化,拿来即走

我们将任务封装为 QRunnable,丢给全局线程池。因为 QRunnable 不是 QObject,无法直接使用信号槽,这里我们使用 C++11 的 std::function 回调来返回结果。

核心特征:线程复用、无事件循环、适合一次性短任务、需手动处理结果回传。

#include <QCoreApplication>
#include <QThreadPool>
#include <QRunnable>
#include <QDebug>
// 1. 定义任务类
class ComputeTask : public QRunnable {
public:
    ComputeTask(int id, std::function<void(int, int)> callback) 
        : m_id(id), m_callback(callback) 
    {
        // 设置任务执行完后自动销毁,防止内存泄漏
        setAutoDelete(true); 
    }
    // 重写 run 函数
    void run() override {
        qDebug() << "Task" << m_id << "running in thread:" << QThread::currentThreadId();
        QThread::msleep(1000); // 模拟耗时操作
        int result = m_id * 100;
        // QRunnable 没有信号槽,使用回调函数将结果传回
        if (m_callback) {
            m_callback(m_id, result);
        }
    }
private:
    int m_id;
    std::function<void(int, int)> m_callback;
};
int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    qDebug() << "Main thread:" << QThread::currentThreadId();
    // 设置全局线程池最大线程数为 2(为了演示线程复用)
    QThreadPool::globalInstance()->setMaxThreadCount(2);
    for (int i = 0; i < 5; ++i) {
        // 定义回调函数接收结果
        auto callback = [](int id, int result) {
            qDebug() << "  -> Got result from Task" << id << ":" << result;
        };
        ComputeTask *task = new ComputeTask(i, callback);
        // 丢给线程池执行
        QThreadPool::globalInstance()->start(task);
    }
    // 等待所有任务完成再退出(仅为了演示,实际GUI程序不推荐主线程wait)
    QThreadPool::globalInstance()->waitForDone();
    qDebug() << "All tasks finished.";
    return 0; // 不需要 a.exec(),因为没有事件循环
}

代码运行结果:

第三层:QtConcurrent —— 高层函数式并发(管理“结果”)

当你要处理的不仅是任务,而是数据集合(比如一个 List 里的所有元素都要做相同操作),QRunnable 写起来还是很啰嗦(要写类、要重写run、要处理数据传递)。于是进阶到了 QtConcurrent

#include <QCoreApplication>
#include <QtConcurrent>
#include <QFutureWatcher>
#include <QDebug>
int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    qDebug() << "Main thread:" << QThread::currentThreadId();
    // 同样限制全局线程池大小以便观察
    QThreadPool::globalInstance()->setMaxThreadCount(2);
    // 使用 QFutureWatcher 监听异步结果(因为 QtConcurrent 本身是非阻塞的)
    QFutureWatcher<int> *watcher = new QFutureWatcher<int>();
    // 当单个结果就绪时触发
    QObject::connect(watcher, &QFutureWatcher<int>::resultReadyAt, [](int index) {
        qDebug() << "  -> Result at index" << index << "is ready.";
    });
    // 当所有任务完成时触发
    QObject::connect(watcher, &QFutureWatcher<int>::finished, [watcher]() {
        qDebug() << "All concurrent tasks finished.";
        // 获取所有结果
        QFuture<int> future = watcher->future();
        for (int i = 0; i < future.resultCount(); ++i) {
            qDebug() << "    Final Result[" << i << "] =" << future.resultAt(i);
        }
        watcher->deleteLater();
        QCoreApplication::quit(); // 退出程序
    });
    // 使用 QtConcurrent::run 启动并发任务
    // 返回 QFuture 对象用于跟踪状态
    QFuture<int> future = QtConcurrent::mapped(QList<int>() << 0 << 1 << 2 << 3 << 4, [](int id) -> int {
        qDebug() << "Concurrent task" << id << "running in thread:" << QThread::currentThreadId();
        QThread::msleep(1000); // 模拟耗时
        return id * 100;       // 直接返回结果
    });
    // 将 future 交给 watcher 监控
    watcher->setFuture(future);
    return a.exec();
}

代码运行结果:

四、对比小结

维度QThreadQThreadPool + QRunnableQtConcurrent
控制粒度线程级(最强)任务级数据级(最弱)
开发效率较低(需手动管理生命周期)中等(需封装任务类)极高(一行代码/Lambda)
事件循环支持(核心优势)不支持(默认无)不支持
信号槽完美支持不原生支持不支持(通过QFuture获取结果)
底层依赖操作系统 API依赖 QThread依赖 QThreadPool
核心场景常驻后台、需事件循环大量独立短任务数据集合的并行计算

从代码看差异

  1. 代码量与复杂度
    • QThread 代码最臃肿。你需要管理 QObjectQThread 的创建、moveToThread、信号槽连接,以及极其重要的内存清理逻辑finished -> deleteLater)。
    • QRunnable 适中。你需要继承并重写 run(),最关键的是要自己想办法把结果“传出来”(如回调、QMetaObject::invokeMethod),容易写出回调地狱。
    • QtConcurrent 最清爽。不用写类,不用管线程,一行 runmapped 搞定,配合 QFutureWatcher 获取结果非常优雅。
  2. 结果获取方式
    • QThread:通过信号槽,天然线程安全,完美融入 Qt 事件循环。
    • QRunnable:通过回调/std::function,或者在 run() 里用 QMetaObject::invokeMethod 往主线程发信号,略显生硬。
    • QtConcurrent:通过 QFuture / QFutureWatcher,这是 Qt 提供的高层抽象,不仅能拿结果,还能监听进度、取消任务。
  3. 生命周期与线程复用(重点体会)
    • QThread 示例中,5 个任务创建了 5 个真实的系统线程,任务完成后线程退出销毁。
    • QRunnableQtConcurrent 示例中,我们设置了 setMaxThreadCount(2)。你会在运行输出中发现,5 个任务只创建了 2 个线程!前 2 个任务跑完后,线程没死,接着跑后 2 个,实现了线程复用,大幅节省了系统资源。

选择建议:

到此这篇关于Qt中多线程QThread、QThreadPool、QConCurrent三种方式对比分析的文章就介绍到这了,更多相关Qt 多线程QThread、QThreadPool、QConCurrent区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文