亲宝软件园·资讯

展开

Qt线程池QThreadPool

欧特克_Glodon 人气:3

一、目的

  现在所有的高性能服务器程序,几乎都会使用到线程池技术,从而更好且有效的榨干服务器性能。而创建并销毁线程的过程势必会消耗内存。而在日常开发中内存资源是及其宝贵的,所以QT 多线程之线程池QThreadPool就有很大用处了。它可以用来管理线程的优先顺序,防止创建过多的线程,起到很好的管理作用。

二、最优线程数

  线程的创建和销毁是有性能开销的,当我们有少量业务需要处理时,我们可以放到线程中完成,甚至可以多开几个线程并行处理。
那么,问题来了,如果需要海量的数据处理,难道无休止的开线程下去吗?

首先,要明白CPU的性能是有限的,每个线程好比一个处理时间片,多个线程之间切换处理,CPU线程上下文来回切换,这个也是需要消耗时间的。所以,物极必反,当线程数量到达一个点后,可能消耗在线程切换的时间,会大于实际线程处理业务的时间,这个可以想象的到。

那么很容易明白:线程数并不是越多越好,而是某个范围或者某个经验值。

一般来讲,我们可以认为,最佳性能线程数==CPU逻辑核心数量,比如CPU是4核8线程,那么开8个线程可以达到性能最佳。
一般电脑是开启超线程的,也就是4核可以模拟出8个逻辑核,故称4核8线程。
QThreadPool线程池默认最大线程数,也是CPU逻辑Core的数量。
严格意义来讲,最佳线程数还与处理业务类型有关,如业务属于IO密集型、CPU密集型,根据经验推断:

三、线程池的原理

最佳性能线程数可以认为等于CPU逻辑核心数量N,所以我们设计程序,为了得到更好的性能,需要实现如下的需求:

上述问题,高度封装的QThreadPool线程池可以解决。

线程池的优点:

四、QThreadPool线程池

下面是QThreadPool的常用函数:

int activeThreadCount() const //当前的活动线程数量
 
void clear()//清除所有当前排队但未开始运行的任务
 
int expiryTimeout() const//线程长时间未使用将会自动退出节约资源,此函数返回等待时间
 
int maxThreadCount() const//线程池可维护的最大线程数量
 
void releaseThread()//释放被保留的线程
 
void reserveThread()//保留线程,此线程将不会占用最大线程数量,从而可能会引起当前活动线程数量大于最大线程数量的情况
 
void setExpiryTimeout(int expiryTimeout)//设置线程回收的等待时间
 
void setMaxThreadCount(int maxThreadCount)//设置最大线程数量
 
void setStackSize(uint stackSize)//此属性包含线程池工作线程的堆栈大小。
 
uint stackSize() const//堆大小
 
void start(QRunnable *runnable, int priority = 0)//加入一个运算到队列,注意start不一定立刻启动,只是插入到队列,排到了才会开始运行。需要传入QRunnable ,后续介绍
 
bool tryStart(QRunnable *runnable)//尝试启动一个
 
bool tryTake(QRunnable *runnable)//删除队列中的一个QRunnable,若当前QRunnable 未启动则返回成功,正在运行则返回失败
 
bool waitForDone(int?<i>msecs</i>?=?-1)//等待所有线程运行结束并退出,参数为等待时间-1表示一直等待到最后一个线程退出

QRunnable类:所有runable对象的基类。
QRunnable类是一个接口, 用于表示需要执行的任务或代码段, 具体任务在run() 函数内部实现。可以使用QThreadPool在各个独立的线程中执行代码。如果autoDelete() 返回true (默认值), QThreadPool将自动删除QRunnable 。使用setAutoDelete() 可更改是否自动删除。
QThreadPool 是创建线程池函数,QRunnable是线程池的线程具体执行操作函数,两者要搭配使用。

五、QThreadPool简单示例

执行效果如下:

#include <QCoreApplication>
#include <QThreadPool>
#include <QDebug>

class Task1 : public QRunnable
{
public:
    Task1()
    { }
    virtual ~Task1() override
    {
        qDebug() << "~Task1()";
    }

    virtual void run() override
    {
        qDebug() << "do Task1 work:" << QThread::currentThreadId();
    }
};

class Task2 : public QRunnable
{
public:
    Task2()
    { }
    virtual ~Task2() override
    {
        qDebug() << "~Task2()";
    }

    virtual void run() override
    {
        qDebug() << "do Task2 work:" << QThread::currentThreadId();
    }
};

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

    Task1* task1 = new Task1();
    Task2* task2 = new Task2();

    QThreadPool threadPool;
    threadPool.start(task1);
    threadPool.start(task2);
    threadPool.waitForDone();

    return a.exec();
}

注意:
线程池使用时传入继承于的QRunnable类对象(并启动该线程对象),并且线程池会自主释放在其中的线程(提高程序性能),还能实现并发,提高效率;不过不能使用信号槽进行通信,需要使用QMetaObject::invokeMethod进行通信。

加载全部内容

相关教程
猜你喜欢
用户评论