QT 多线程之线程池QThreadPool

举报
华为云 断点 发表于 2023/02/16 20:47:12 2023/02/16
【摘要】 QT多线程编程系列专栏文章共有12篇,全面的讲述、实现、运行了QT多线程的各种操作,包括运行原理、线程、进程、多线程、锁、QMutex、QSemaphore、 Emit、Sgnals、Slot、QWaitCondition、线程事件循环、QObjects、重入与线程安全、主线程子线程互相传值、线程同步与异步处理、线程的使用、浅拷贝、深拷贝、隐式共享、隐式共享机制对STL样式迭代器的影响等等文

   QT多线程编程系列专栏文章共有12篇,全面的讲述、实现、运行了QT多线程的各种操作,包括运行原理、线程、进程、多线程、锁、QMutex、QSemaphore、 Emit、Sgnals、Slot、QWaitCondition、线程事件循环、QObjects、重入与线程安全、主线程子线程互相传值、线程同步与异步处理、线程的使用、浅拷贝、深拷贝、隐式共享、隐式共享机制对STL样式迭代器的影响等等文章。

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

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

本文作者原创首发于CSDN,本文原创请勿转载

版权声明:本文为CSDN博主「双子座断点」的原创文章,遵循CC 4.0 BY-SA版权协议。
原文链接:https://blog.csdn.net/qq_37529913/article/details/110127940

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

3. 程序基本界面

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 <QRunnable>
#include <QObject>
#include <QDebug>
#include <QReadWriteLock>
#include <QTime>
#include <QSemaphore>
 
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 <QMutex>
#include <QRandomGenerator>
#include <QWaitCondition>
#include <QThread>
/*
如果乱码就加上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打印数据:") <<QString::number(i);
    }
    //emit isDone(1);  //发送完成信号
}
void mythread::recMegFromMain(QString str)
{
    qDebug()<< "子线程接收到" <<str;
}
 
void mythread1::run()
{
 
    qDebug()<< QStringLiteral("PrintTask run 被调用,调用线程ID为:") << QThread::currentThread() ;
    for(int i=10;i<20;i++){
        QThread::sleep(2);
        qDebug()<< "线程2打印数据:" <<QString::number(i);
    }
    //emit isDone(1);  //发送完成信号
}
 
void mythread2::run()
{
 
    qDebug()<< "PrintTask run 被调用,调用线程ID为:" << QThread::currentThread() ;
    for(int i=20;i<30;i++){
        QThread::sleep(2);
        qDebug()<< "线程3打印数据:" <<QString::number(i);
    }
    //emit isDone(1);  //发送完成信号
}
 
void mythread3::run()
{
 
    qDebug()<< "PrintTask run 被调用,调用线程ID为:" << QThread::currentThread() ;
    for(int i=30;i<40;i++){
        QThread::sleep(2);
        qDebug()<< "线程4打印数据:" <<QString::number(i);
    }
    //emit isDone(1);  //发送完成信号
}
 
void mythread4::run()
{
 
    qDebug()<< "PrintTask run 被调用,调用线程ID为:" << QThread::currentThread() ;
    for(int i=40;i<50;i++){
        QThread::sleep(2);
        qDebug()<< "线程5打印数据:" <<QString::number(i);
    }
    //emit isDone(1);  //发送完成信号
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
#include "mythread.h"
#include <QDebug>
#include <QMessageBox>
#include <QThreadPool>
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 <QReadWriteLock>
 
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()<<str;
}
void MainWindow::dealDone()
{
    ui->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() <<pool.activeThreadCount();
    //sengMsgToThreadBtn();
}
 
void MainWindow::on_pushButton_4_clicked()
{
    pool.start(task);             //任务放进线程池
    task->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

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。