QT 多线程之线程池QThreadPool(深入理解)

在一个应用程序中,我们需要多次使用线程,也就意味着,我们需要多次创建并销毁线程。而创建并销毁线程的过程势必会消耗内存。而在日常开发中,内存资源是及其宝贵的,所以,我们这里就有了本篇文章QT 多线程之线程池QThreadPool。在程序逻辑中经常会碰到需要处理大批量任务的情况,比如密集的网络请求,或者日志分析等等。一般会创建一个队列,用一个或者多个线程去消费这个队列,一般也要处理队列的加锁和解锁的问题。

详解QThreadPool线程池过程中,给大家举栗出线程池优点和运行的注意事项以及示例Demo。

QT 多线程之线程池QThreadPool(深入理解)_第1张图片

本文作者原创,转载请附上文章出处与本文链接。

QT 多线程之线程池QThreadPool(深入理解)目录

1. 线程池的优点

2. QT线程池函数

3. 程序基本界面

4. 类说明

QThreadPool类:

QRunnable类:

5. 示例Demo

6. 其它线程文章


1. 线程池的优点

  • 创建和销毁线程需要和OS交互,少量线程影响不大,但是线程数量太大,势必会影响性能,使用线程池可以这种开销;
  • 线程池维护一定数量的线程,使用时,将指定函数传递给线程池,线程池会在线程中执行任务;

2. QT线程池函数

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?msecs?=?-1)//等待所有线程运行结束并退出,参数为等待时间-1表示一直等待到最后一个线程退出

3. 程序基本界面

QT 多线程之线程池QThreadPool(深入理解)_第2张图片

4. 类说明

QThreadPool类:

用来管理 QThreads,经过测试QThreadPool线程池函数并不是安全线程,多个线程操作还是会出现抢资源现象,同步还是需要互斥锁或者信号量来同步。

主要属性:

1、activeThreadCount: 此属性表示线程池中的活动线程数,通过activeThreadCount() 调用。
2、expiryTimeout: 线程活着的时间。没有设置expiryTimeout毫秒的线程会自动退出,此类线程将根据需要重新启动。默认的expiryTimeout为30000毫秒 (30 秒)。如果expiryTimeout为负, 则新创建的线程将不会过期, 在线程池被销毁之前, 它们将不会退出。通过expiryTimeout()调用,通setExpiryTimeout(int expiryTimeout)设置 。
3、maxThreadCount : int 表示线程池使用的最大线程数。
通过maxThreadCount() 调用,通过setMaxThreadCount(int maxThreadCount) 设置
注意:即使maxThreadCount限制为零或为负数, 线程池也至少有1个线程。

主要成员函数

QThreadPool *QThreadPool::globalInstance()

返回Qt应用程序全局线程池实例。

void reserveThread()

预约一个线程,这个函数总是会增加活动线程的数量。这意味着通过使用这个函数,activeThreadCount()可以返回一个大于maxThreadCount()的值。

void releaseThread()

释放以前通过调用reserveThread()预约的线程。
如果不先预约一个线程,调用这个函数会临时增加maxThreadCount()。当线程进入休眠等待时,能够允许其他线程继续。
要记得在完成等待时调用reserveThread(),以便线程池可以正确控制activeThreadCount()。

void QThreadPool :: start(QRunnable * runnable,int priority = 0)

在任务数量小于maxThreadCount时,为每个runnable任务预约一个线程。超过maxThreadCount时,将任务放入运行队列中。priority 参数用来设置线程运行优先级。

bool tryStart(QRunnable *runnable)

此方法尝试预约一个线程来运行runnable。
如果在调用的时候没有线程可用,那么这个函数什么都不做,并返回false。否则,将使用一个可用线程立即运行runnable,并返回此函数true。

void clear()

用于删除在任务队列中,还没有启动的任务。

bool tryTake(QRunnable *runnable)

如果runnable任务还没开始运行,那么从队列中删除此runable任务,此时函数返回true;如果runnable任务已经运行,返回false。
只用来删除runnable->autoDelete() == false的runnable任务,否则可能会删错任务.

bool waitForDone(int msecs = -1)

等待msecs毫秒, 以便所有线程退出并从线程池中移除所有线程。如果删除了所有线程, 则返回true ,否则, 它将返回false。默认等待时间为-1,即等待最后一个线程退出。

内容出自: https://blog.csdn.net/y396397735/article/details/78637634

QRunnable类:

是所有runable对象的基类。
QRunnable类是一个接口, 用于表示需要执行的任务或代码段, 具体任务在run() 函数内部实现。

可以使用QThreadPool在各个独立的线程中执行代码。如果autoDelete() 返回true (默认值), QThreadPool将自动删除QRunnable 。使用setAutoDelete() 可更改是否自动删除。

QThreadPool 是创建线程池函数,QRunnable是线程池的线程具体执行操作函数。两者要搭配使用。

主要成员函数

bool autoDelete() const

获取自动删除是否启用,启用返回true,未启用返回false。

virtual void run() = 0

纯虚函数,在QRunnable子类中实现详细任务处理逻辑。

void setAutoDelete(bool autoDelete)

如果autoDelete为 true, 则启用自动删除。否则自动删除将被禁用。
如果启用了自动删除, QThreadPool将在调用 run () 函数返回后自动删除此runable对象。否则, runable对象所有权不属于线程池,由开发人员管理。
请注意, 必须先设置此标志,(默认构造函数已经将其设置为true),然后才能调用QThreadPool:: start()。在QThreadPool:: start() 之后调用此函数将导致不可预测后果。

内容出自:https://blog.csdn.net/y396397735/article/details/78637634

5. 示例Demo

mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include 
#include 
#include 
#include 
#include 
#include 

class mythread : public QObject, public QRunnable
{
        Q_OBJECT
public:
    mythread();


    //QThread的虚函数
    //线程处理函数
    //不能直接调用,通过start()间接调用
    void run();

signals:
    void isDone(int);   //处理完成信号
    void mySignal();    //注意!要使用信号,采用QObejct 和 QRunnable多继承,记得QObject要放在前面

public slots:
    //接收主线程的消息
    void recMegFromMain(QString);
};

class mythread1 : public QObject, public QRunnable
{
    Q_OBJECT
public:

    void run();

};

class mythread2  : public QObject, public QRunnable
{
    Q_OBJECT
public:

    void run();
};


class mythread3  : public QObject, public QRunnable
{
    Q_OBJECT
public:

    void run();
};

class mythread4  : public QObject, public QRunnable
{
    Q_OBJECT
public:

    void run();
};
#endif // MYTHREAD_H

mythread.cpp

#include "mythread.h"
#include 
#include 
#include 
#include 
/*
如果乱码就加上QStringLiteral();

#pragma execution_character_set("GB2312")
*/
mythread::mythread()
{

}
void mythread::run()
{


    for(int i=0;i<10;i++){
        //QThread::sleep(2);
        qDebug()<< QStringLiteral("PrintTask run 被调用,调用线程ID为:") << QThread::currentThread() << QStringLiteral( " 线程1打印数据:") <

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include "mythread.h"
#include 
#include 
#include 
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void dealDone();   //线程槽函数

    void mySlot();

    void receiveMsgFromThread(int);

    void sengMsgToThreadBtn();
private:
    Ui::MainWindow *ui;

    QThreadPool pool;

    mythread* task = new mythread();
    mythread1* task1 = new mythread1();
    mythread2* task2 = new mythread2();
    mythread3* task3 = new mythread3();
    mythread4* task4 = new mythread4();
signals:
    //给子线程发消息
    void sengMsgToThread(QString);
private slots:
    void on_pushButton_clicked();
    void on_pushButton_2_clicked();
    void on_pushButton_3_clicked();
    void on_pushButton_4_clicked();
    void on_pushButton_5_clicked();
    void on_pushButton_6_clicked();
    void on_pushButton_7_clicked();
    void on_pushButton_8_clicked();
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include 

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //设置最大线程数为3的一个线程池
    pool.setMaxThreadCount(5);

}

MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::sengMsgToThreadBtn()
{
    emit sengMsgToThread("hello");
}
// 定义槽函数 mySlot()
void MainWindow::mySlot()
{
    QMessageBox::about(this,"Tsignal", "响应线程中的mySlot函数");
}
//接收线程函数
void MainWindow::receiveMsgFromThread(int i)
{
    QString str = QString::number(i);
    qDebug()<label->setText("线程停止");
    //停止线程

}

void MainWindow::on_pushButton_clicked()
{

    //QThread::sleep(1);
    //pool.waitForDone();           //等待任务结束
    on_pushButton_4_clicked();
    on_pushButton_5_clicked();
    on_pushButton_6_clicked();
    on_pushButton_7_clicked();
    on_pushButton_8_clicked();

    //启动线程,处理数据
    ui->label->setText("start");

}

void MainWindow::on_pushButton_2_clicked()
{
    //pool.releaseThread();
    //停止线程
    //dealDone();
    //sengMsgToThreadBtn();
}

void MainWindow::on_pushButton_3_clicked()
{
    qDebug() <setAutoDelete(false);
}

void MainWindow::on_pushButton_5_clicked()
{
    pool.start(task);             //任务放进线程池
    task->setAutoDelete(false);
    /*
    pool.start(task1);
    task1->setAutoDelete(false);
    */
}

void MainWindow::on_pushButton_6_clicked()
{
    pool.start(task);             //任务放进线程池

    //pool.start(task2);
}

void MainWindow::on_pushButton_7_clicked()
{
    pool.start(task);             //任务放进线程池
    //pool.start(task3);
}

void MainWindow::on_pushButton_8_clicked()
{
    pool.start(task);             //任务放进线程池
    //pool.start(task4);
}

这个已经是比较完整的项目了,下载地址:QT_ThreadPool.rar_qt线程池-C++文档类资源-CSDN下载

6. 其它线程文章

以下文章均为作者原创文章,看完记得收藏、关注加

QT 初识线程(简单实现):https://blog.csdn.net/qq_37529913/article/details/110127940

QT QMutex使用详解:https://blog.csdn.net/qq_37529913/article/details/110187452

QT 线程之QSemaphore(深入理解):https://blog.csdn.net/qq_37529913/article/details/110187121

QT线程 Emit、Sgnals、Slot详细解释:https://blog.csdn.net/qq_37529913/article/details/110211435

QT 线程之QWaitCondition(深入理解):https://blog.csdn.net/qq_37529913/article/details/110212704

Qt 多线程之线程事件循环(深入理解):https://blog.csdn.net/qq_37529913/article/details/110229382

QT线程之QObjects(深入理解):https://blog.csdn.net/qq_37529913/article/details/110228837

QT线程之可重入与线程安全(深入理解):https://blog.csdn.net/qq_37529913/article/details/110224166

QT 主线程子线程互相传值:QT 主线程子线程互相传值_双子座断点的博客-CSDN博客_qt主线程给子线程传数据

QT线程同步与异步处理:QT线程同步与异步处理_双子座断点的博客-CSDN博客_qt异步线程

QT 多线程之线程池QThreadPool(深入理解):QT 多线程之线程池QThreadPool(深入理解)_双子座断点的博客-CSDN博客_qthreadpool

QT之浅拷贝、深拷贝、隐式共享(深度理解必看文章):https://blog.csdn.net/qq_37529913/article/details/110235596

QT 隐式共享机制对STL样式迭代器的影响:https://blog.csdn.net/qq_37529913/article/details/110252454

你可能感兴趣的:(#,QT开发之路,多线程编程,QT,QThreadPool,QT线程池,多线程)