总结了两篇关于Qt线程的文章,刚想起来,还有线程池也属于线程知识相关。所以又补充一篇。
QThreadPool是Qt框架中的一个组件,它主要用于管理和复用线程资源,以优化并发任务的执行效率。当你有大量短小的任务需要异步执行时,使用QThreadPool可以避免频繁创建和销毁线程所带来的开销,并能轻松控制同时运行的任务数量。
QThreadPool的工作原理:
-
线程管理:
- QThreadPool维护了一个可复用的线程列表。
- 当有新的QRunnable任务提交到线程池时,线程池会选择一个可用(即未执行任务)的线程来执行任务,如果没有可用线程并且线程总数没有达到预设的最大线程数,那么线程池将会创建一个新的线程来执行任务。
-
任务调度:
- 提交到线程池的任务必须是QRunnable的子类实例,每个任务都需要重写run()虚函数,其中包含实际的执行逻辑。
- QThreadPool按照先进先出(FIFO)的原则处理任务,除非任务优先级不同,高优先级的任务可能会提前执行。
-
线程限制:
- 开发者可以调用QThreadPool::setMaxThreadCount(int maxThreadCount)来设置线程池允许的最大线程数量,超过此数量的任务将排队等待。
使用QThreadPool的步骤与示例代码:
以下是一个简单的QThreadPool使用示例:
cpp
#include <QCoreApplication>
#include <QThreadPool>
#include <QDebug>
#include <QRunnable>
class MyTask : public QRunnable {
public:
void run() override {
qDebug() << "Task is running on thread:" << QThread::currentThreadId();
// 这里放置具体任务执行的代码
// ...
// 假设这是一个耗时操作
QThread::sleep(1);
qDebug() << "Task finished.";
}
};
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QThreadPool pool;
// 设置最大线程数(如果不设置,默认不限制线程数量)
pool.setMaxThreadCount(4);
for (int i = 0; i < 10; ++i) {
// 创建任务实例
MyTask *task = new MyTask;
// 提交任务到线程池
pool.start(task);
// 因为task的autoDelete属性为默认值(true),所以任务完成后,QThreadPool会自动删除任务对象
}
// 等待所有任务完成
pool.waitForDone();
return app.exec();
}
在这个例子中,我们定义了一个名为MyTask的类,它继承自QRunnable并重写了run()方法。然后我们创建了10个这样的任务实例,并提交到线程池中执行。由于设置了最大线程数为4,所以最多只有4个任务会并行执行,其余任务会在队列中等待直到有线程空闲。最后,我们调用waitForDone()来确保所有的任务都已完成再退出主程序。
QRunnable与QThread的对比说明
上边例子中出现了QRunnable的使用。特此对比QThread做一下补充说明。
QRunnable和QThread都是Qt库中用于实现多线程的工具,它们各自有特定的设计目标和使用场景,下面是对两者之间对比的详细说明:
QThread:
- QThread是一个跨平台的线程类,它是QObject的一个子类,因此可以和其他QObject一样使用信号和槽进行通信,这是QThread的一大优势。
- 使用QThread一般有两种方式:一是通过继承QThread类并重写run()方法,然后调用start()启动线程;二是将工作逻辑封装在另外的QObject中,并将这个QObject移动到新创建的QThread实例的事件循环中,这样工作逻辑将在新线程的上下文中执行。
- 当你继承QThread并重写run()方法时,实际上是在创建一个具有自己事件循环的线程,这种情况下线程不会在run()方法执行完毕后立即结束,而是会继续监听事件队列。
- QThread适合长时间运行或者需要持续存在并可能随时处理事件的线程,例如后台服务、定时器等。
QRunnable:
QRunnable是一个轻量级的非QObject类,设计初衷是为了简化临时性、一次性任务的执行,不需要像QThread那样建立完整的对象层次结构。
- QRunnable只包含一个纯虚函数void run(),你需要继承QRunnable并实现这个run()函数,用来定义线程的具体工作内容。
- QRunnable配合QThreadPool使用可以实现任务的并发执行。线程池负责调度和管理线程资源,当一个QRunnable任务提交给线程池时,池中的一个线程会执行该任务的run()方法,完成后线程将回到池中等待下一个任务。
- QRunnable不适合需要长期存在的线程,因为它不支持信号和槽机制,这意味着无法像QThread那样直接在线程间发送信号来同步数据或通知事件。
- QRunnable更适合执行短暂、独立、计算密集型的任务,特别是当任务数量巨大且可以很好地并行化时,通过QThreadPool能够有效地利用系统资源。
总结来说,QThread更适合构建复杂的多线程应用,特别是在需要使用信号槽机制来协调线程间交互的情况下;而QRunnable则更适合处理一次性、无状态、易于并行化的任务,它可以高效地利用线程池,减少系统资源消耗并提高并发性能。