Qt 之 QThreadPool 和 QRunnable

本文详细介绍了Qt中的QRunnable和QThreadPool。QRunnable是表示任务的接口,需要重写run()函数。QThreadPool管理线程池,支持多次执行QRunnable,但多次调用start()可能引发问题。QThreadPool的线程会根据设置自动删除或到期。基本使用包括子类化QRunnable并实现run(),然后通过QThreadPool::start()启动。文章还提到,QRunnable不支持直接的信号/槽,可以继承QObject来实现。最后,文章提到了线程的关闭和线程间通信的几种方式。

作者: 一去、二三里
个人微信号: iwaleon
微信公众号: 高效程序员

QRunnable 是所有 runnable 对象的基类,而 QThreadPool 类用于管理 QThreads 集合。

QRunnable 类是一个接口,用于表示一个任务或要执行的代码,需要重新实现 run() 函数。

QThreadPool 管理和循环使用单独的 QThread 对象,以帮助程序减少创建线程的成本。每个 Qt 应用程序都有一个全局 QThreadPool 对象,可以通过调用 globalInstance() 访问。

详细描述

QThreadPool 支持多次执行相同的 QRunnable,通过调用 QThreadPool::tryStart(this) 从 run() 函数内。如果启用了 autoDelete,当最后一个线程退出 run() 函数,QRunnable 将被删除。多次调用 QThreadPool::start() 使用相同的 QRunnable,当启用 autoDelete 时会创建一个竞争条件,不推荐使用。

一定时间未使用线程将会到期,默认到期超时是 30000 毫秒(30秒)。可以使用 setExpiryTimeout() 来改变,设定一个负值,则会禁用到期机制。

调用 maxThreadCount() 查询使用线程的最大数量,如果需要,可以使用 setMaxThreadCount() 进行更改。默认情况下,maxThreadCou

### ### 数据池设计与QThreadPool/QRunnable的关系 在Qt中,`QThreadPool``QRunnable`是实现并发任务的核心组件。`QRunnable`是一个轻量级的非QObject类,设计用于执行一次性任务,其核心方法是`run()`。`QThreadPool`负责管理线程资源,并调度`QRunnable`任务在多个线程中并发执行。通过结合这两个类,可以实现一个高效的数据池系统,支持多线程并发访问处理数据[^2]。 ### ### 数据池类设计 数据池类用于存储管理共享数据,通常使用线程安全的数据结构,并配合互斥锁(`QMutex`)或读写锁(`QReadWriteLock`)来确保线程安全。以下是一个简单的线程安全数据池类的实现: ```cpp class DataPool { public: void addData(const QString& data) { QMutexLocker locker(&mutex); dataList.append(data); } QString getData() { QMutexLocker locker(&mutex); if (dataList.isEmpty()) { return QString(); } return dataList.takeFirst(); } bool hasData() const { QMutexLocker locker(const_cast<QMutex*>(&mutex)); return !dataList.isEmpty(); } private: QList<QString> dataList; mutable QMutex mutex; }; ``` 该类提供了添加数据、获取数据以及检查是否有数据的方法,并使用`QMutexLocker`来确保线程安全。 ### ### 实现QRunnable任务类 为了在`QThreadPool`中执行数据池的操作,需要定义一个继承自`QRunnable`的任务类,并在`run()`方法中实现具体的业务逻辑。例如,以下是一个生产者任务类,它将数据添加到数据池中: ```cpp class ProducerTask : public QRunnable { public: ProducerTask(DataPool* pool, int taskId) : dataPool(pool), taskId(taskId) {} void run() override { for (int i = 0; i < 10; ++i) { QString data = QString("Task %1: Data %2").arg(taskId).arg(i); dataPool->addData(data); QThread::msleep(50); // 模拟耗时操作 } } private: DataPool* dataPool; int taskId; }; ``` 该任务类在`run()`方法中循环生成数据并将其添加到数据池中。 ### ### 启动任务并操作数据池 在主线程中,可以创建多个`ProducerTask`任务并提交给`QThreadPool`执行。线程池会自动分配线程来运行这些任务,从而实现并发的数据写入操作。 ```cpp int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); DataPool pool; QThreadPool* threadPool = QThreadPool::globalInstance(); // 创建并提交多个任务 for (int i = 0; i < 5; ++i) { QRunnable* task = new ProducerTask(&pool, i); threadPool->start(task); } // 等待所有任务完成 threadPool->waitForDone(); // 打印最终数据池内容 while (pool.hasData()) { qDebug() << pool.getData(); } return app.exec(); } ``` 上述代码中,使用`QThreadPool::globalInstance()`获取全局线程池实例,并通过`start()`方法提交任务。线程池会自动选择空闲线程执行任务。 ### ### 信号与槽机制的扩展 虽然`QRunnable`不支持信号与槽机制,但可以通过将任务逻辑封装在QObject派生类中,并在`QRunnable`任务中调用该对象的方法来实现线程间通信。例如,可以创建一个数据处理对象,并在任务中调用其方法: ```cpp class DataProcessor : public QObject { Q_OBJECT public: explicit DataProcessor(DataPool* pool) : dataPool(pool) {} public slots: void process() { while (dataPool->hasData()) { QString data = dataPool->getData(); if (!data.isEmpty()) { qDebug() << "Processing data:" << data; } } } private: DataPool* dataPool; }; ``` 然后在`QRunnable`任务中使用`QMetaObject::invokeMethod`调用该槽函数: ```cpp class ConsumerTask : public QRunnable { public: ConsumerTask(DataPool* pool) : dataPool(pool) {} void run() override { DataProcessor processor(dataPool); QMetaObject::invokeMethod(&processor, "process", Qt::QueuedConnection); } private: DataPool* dataPool; }; ``` 这种方式可以在不直接使用`QThread`的情况下实现线程间通信。 ### ### 线程池管理与性能优化 `QThreadPool`支持设置最大线程数、任务优先级等参数,可以优化线程资源的使用。例如: ```cpp threadPool->setMaxThreadCount(4); // 设置最大线程数为4 ``` 此外,`QRunnable`任务支持自动删除机制,默认情况下线程池会在任务完成后自动删除任务对象。可以通过`setAutoDelete(false)`来禁用自动删除,以避免内存泄漏。 ###
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一去丶二三里

有收获,再打赏!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值