QT6线程池的使用QThreadPool

QT6线程池的使用QThreadPool

在这里插入图片描述

前面介绍了多线程的使用,这次主要介绍线程池的使用。
线程池主要解决了两个问题:
1、因为过多的new thread会占用太多的资源,最终可能会导致服务器因资源不足而宕机;

2、频繁的创建、销毁线程也会消耗服务器性能而最终影响了程序的执行效率。

简单解释就是不固定数量的new thread可能会把机器的内存、网络带宽等资源消耗干净,或者每1或几分钟(秒钟)周期内(反复)执的线程new和delete会影响执行效率。

如果出现以上的两种情况,就推荐使用线程池来管理线程。那么具体应该如何使用Qt的线程池呢?

实现多线程一共有六步

  1. 创建任务类,继承QRunable和QObject
  2. 实现任务类,重写run()方法
  3. 设定任务类是否自动释放内存
  4. 在主线程中,创建任务对象
  5. 设置最大线程数setMaxThreadCount
  6. 使用start()启动线程

本案例的流程图

循环=1000
返回毫秒值
循环=2000
返回毫秒值
循环计算值
点击开始按钮
线程1
相加线程1与线程2返回值
线程2
线程3
返回新的毫秒值

1、创建任务类

  • 定义了一个任务类,在Task中,实现循环N次,返回M毫秒
  • 继承QRunable和QObject,重写run()方法

myThread.h,全部代码

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include <QRunnable>

class Task:public QObject, public QRunnable
{
    Q_OBJECT
public:
    explicit Task(QObject *parent = nullptr);
    void receNum(int num);
signals:
    void working(int num);
    void finish(QString elapsedTime);

protected:
    void run() override;
private:
    int iRecvNum;
};
#endif // MYTHREAD_H

2、实现任务类,重写run方法

  • receNum方法,用来接收主线程的参数
  • run方法实现线程内的业务处理
    myThread.cpp,全部代码
#include "myThread.h"
#include <QElapsedTimer>
#include <QDebug>
#include <QThread>

Task::Task(QObject *parent) : QObject(parent), QRunnable()
{
    //设置自动析构
    setAutoDelete(false);

}

void Task::receNum(int num)
{
    iRecvNum=num;

}

void Task::run(){
    qDebug() <<" 线程:"<< iRecvNum<<" || "<<QThread::currentThread();
    QElapsedTimer time;
    time.start();
    for(int i=0;i<iRecvNum;i++){
        QThread::msleep(1); // 处理任务
        emit working(i);
    }
    qDebug() <<"线程,共耗时: "<<time.elapsed() <<" ms。";
    emit finish(QString::number(time.elapsed()));

}

3、设定任务类是否自动释放内存

  • 该设置在上面的myThread.cpp中;
  • 因为项目中定义的是三个任务对象数组,所以设置为flase,默认为true
    //设置自动析构
    setAutoDelete(false);

4. 在主线程中,创建任务对象

在QMainWindow.h文件中

  • 定义三个信号函数(transParam1,transParam1和transParam3),以方便向三个线程内传值,信号函数只需声明,不用实现。
  • 创建了三个线程对象,Task *work =new Task[3];
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "myThread.h"
#include <QThread>
#include <QThreadPool>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private slots:
    void on_startButton_clicked();

signals:
    void transParam1(int num);
    void transParam2(int num);
    void transParam3(int num);


private:
    Ui::MainWindow *ui;

    //创建任务类的对象
    Task *work =new Task[3];
Task *task =new Task;
    int returnValue[2]={0};

};
#endif // MAINWINDOW_H

5、设置最大线程数setMaxThreadCount

在MainWindow.cpp中

  • 通过setMaxThreadCount设置最大线程数,也可以理解为同时执行的最大线程数
  • 设置好最大线程数可以有效利用服务器资源和程序执行效率
	//设置线程数
    QThreadPool::globalInstance()->setMaxThreadCount(1);
}

6. 使用start()启动线程

  • 使用QThreadPool::globalInstance()->start(&work[0])来启动线程
  • 启动线程后,任务对象有可能不会立即执行,而是会根据刚才设置的最大线程数在线程池中排队执行

MainWindow.cpp,全部代码

#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "myThread.h"


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

    QThreadPool::globalInstance()->setMaxThreadCount(1);

    //给子线程传值
    connect(this,&MainWindow::transParam1,&work[0],&Task::receNum);
    connect(this,&MainWindow::transParam2,&work[1],&Task::receNum);
    connect(this,&MainWindow::transParam3,&work[2],&Task::receNum);

    //接收子线程发送的数据,并更新进度条
    connect(&work[0],&Task::working,this,[=](int l){
        ui->thread1_Bar->setValue(l);
        ui->thread1_Bar->update();
    });

    connect(&work[1],&Task::working,this,[=](int l){
        ui->thread2_Bar->setValue(l);
        ui->thread2_Bar->update();
    });
    connect(&work[2],&Task::working,this,[=](int l){
        ui->thread3_Bar->setValue(l);
        ui->thread3_Bar->update();
    });

    connect(&work[0],&Task::finish,this,[=](QString s){
        ui->thread1_Edit->setText(s);
        returnValue[0]=s.toInt();
        if(returnValue[1]>0){
            ui->thread3_Bar->setMaximum(returnValue[0]+returnValue[1]);
            emit transParam3(returnValue[0]+returnValue[1]);
            QThreadPool::globalInstance()->start(&work[2]);
        }

    });

    connect(&work[1],&Task::finish,this,[=](QString s){
        ui->thread2_Edit->setText(s);
        returnValue[1]=s.toInt();
        if(returnValue[0]>0){
            ui->thread3_Bar->setMaximum(returnValue[0]+returnValue[1]);
            emit transParam3(returnValue[0]+returnValue[1]);
            QThreadPool::globalInstance()->start(&work[2]);
        }
    });
    connect(&work[2],&Task::finish,this,[=](QString s){
        ui->thread3_Edit->setText(s);
    });

    connect(ui->startButton,&QPushButton::clicked,this,[=](){
        ui->thread1_Bar->setMaximum(1000);
        ui->thread2_Bar->setMaximum(2000);
        emit transParam1(1000);
        emit transParam2(2000);
        QThreadPool::globalInstance()->start(&work[0]);
        QThreadPool::globalInstance()->start(&work[1]);
    });


}

MainWindow::~MainWindow()
{
    delete[] work;
    QThreadPool::globalInstance()->waitForDone();
    delete ui;
}


void MainWindow::on_startButton_clicked()
{
    for(int i=0;i<2;i++){
        returnValue[i] = 0;
    }

    ui->thread1_Bar->setValue(0);
    ui->thread1_Edit->setText(0);
    ui->thread2_Bar->setValue(0);
    ui->thread2_Edit->setText(0);
    ui->thread3_Bar->setValue(0);
    ui->thread3_Edit->setText(0);
}

执行效果

  • 将线程数设为1时,可以看到三个任务依次执行,线程ID唯一

请添加图片描述

  • 将线程数设为3时,可以看到同时执行了两个任务并且使用了两个线程,第三个任务也被分配到了第二个线程里,节省了资源。

请添加图片描述

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]:Qt中可以有多种使用线程的方式,其中一种是使用QThreadPool线程池QThreadPool提供了一种方便的方式来管理和调度线程。使用线程池可以避免频繁创建和销毁线程的开销,提高了线程的复用性和效率。可以通过以下步骤来使用QThreadPool: 1. 创建一个继承自QRunnable的类,重写其run()函数,该函数中包含了需要在线程中执行的代码。 2. 创建一个QThreadPool对象,并设置最大线程数(可选)。 3. 创建QRunnable对象,并将其添加到线程池中,使用start()函数启动线程。 4. 在需要使用线程的地方,可以通过调用waitForDone()函数来等待线程执行完毕。 需要注意的是,QThreadPool会自动管理线程的生命周期,当线程执行完毕后会自动销毁。同时,QThreadPool还提供了一些其他的函数,如activeThreadCount()用于获取当前活动的线程数,maxThreadCount()用于获取最大线程数等。 总结起来,使用QThreadPool可以方便地管理和调度线程,提高线程的复用性和效率。\[1\] 请注意,引用\[2\]和引用\[3\]是与问题无关的内容,不需要在回答中引用。 #### 引用[.reference_title] - *1* [Qt线程池](https://blog.csdn.net/QtCompany/article/details/130566244)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [QT-线程并发、QTcpServer并发、QThreadPool线程池](https://blog.csdn.net/m0_60259116/article/details/128087525)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值