Qt 中线程池的使用

news2024/9/23 21:25:44

1. 线程池的原理

我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务呢?

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件), 则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

在各个编程语言的语种中都有线程池的概念,并且很多语言中直接提供了线程池,作为程序猿直接使用就可以了,下面给大家介绍一下线程池的实现原理:
线程池的组成主要分为 3 个部分,这三部分配合工作就可以得到一个完整的线程池:

  1. 任务队列,存储需要处理的任务,由工作的线程来处理这些任务。
  • 通过线程池提供的 API 函数,将一个待处理的任务添加到任务队列,或者从任务队列中删除。
  • 已处理的任务会被从任务队列中删除
  • 线程池的使用者,也就是调用线程池函数往任务队列中添加任务的线程就是生产者线程
  1. 工作的线程(任务队列任务的消费者) ,N 个
  • 线程池中维护了一定数量的工作线程,他们的作用是是不停的读任务队列,从里边取出任务并处理
  • 工作的线程相当于是任务队列的消费者角色,
  • 如果任务队列为空,工作的线程将会被阻塞 (使用条件变量 / 信号量阻塞)
  • 如果阻塞之后有了新的任务,由生产者将阻塞解除,工作线程开始工作
  1. 管理者线程(不处理任务队列中的任务),1 个
  • 它的任务是周期性的对任务队列中的任务数量以及处于忙状态的工作线程个数进行检测
  • 当任务过多的时候,可以适当的创建一些新的工作线程
  • 当任务过少的时候,可以适当的销毁一些工作的线程
    在这里插入图片描述

2. QRunnable

在 Qt 中使用线程池需要先创建任务,添加到线程池中的每一个任务都需要是一个 QRunnable 类型,因此在程序中需要创建子类继承 QRunnable 这个类,然后重写 run() 方法,在这个函数中编写要在线程池中执行的任务,并将这个子类对象传递给线程池,这样任务就可以被线程池中的某个工作的线程处理掉了。
QRunnable 类 常用函数不多,主要是设置任务对象传给线程池后,是否需要自动析构。

// 在子类中必须要重写的函数, 里边是任务的处理流程
[pure virtual] void QRunnable::run();

// 参数设置为 true: 这个任务对象在线程池中的线程中处理完毕, 这个任务对象就会自动销毁
// 参数设置为 false: 这个任务对象在线程池中的线程中处理完毕, 对象需要程序猿手动销毁
void QRunnable::setAutoDelete(bool autoDelete);
// 获取当前任务对象的析构方式,返回true->自动析构, 返回false->手动析构
bool QRunnable::autoDelete() const;

创建一个要添加到线程池中的任务类,处理方式如下:

class MyWork : public QObject, public QRunnable
{
    Q_OBJECT
public:
    explicit MyWork(QObject *parent = nullptr)
    {
        // 任务执行完毕,该对象自动销毁
        setAutoDelete(true);
    }
    ~MyWork();

    void run() override{}
}

在上面的示例中 MyWork 类是一个多重继承,如果需要在这个任务中使用 Qt 的信号槽机制进行数据的传递就必须继承 QObject 这个类,如果不使用信号槽传递数据就可以不继承了,只继承 QRunnable 即可。

class MyWork :public QRunnable
{
    Q_OBJECT
public:
    explicit MyWork()
    {
        // 任务执行完毕,该对象自动销毁
        setAutoDelete(true);
    }
    ~MyWork();

    void run() override{}
}

3. QThreadPool

Qt 中的 QThreadPool 类管理了一组 QThreads, 里边还维护了一个任务队列。QThreadPool 管理和回收各个 QThread 对象,以帮助减少使用线程的程序中的线程创建成本。每个Qt应用程序都有一个全局 QThreadPool 对象,可以通过调用 globalInstance() 来访问它。也可以单独创建一个 QThreadPool 对象使用。
线程池常用的 API 函数如下:

// 获取和设置线程中的最大线程个数
int maxThreadCount() const;
void setMaxThreadCount(int maxThreadCount);

// 给线程池添加任务, 任务是一个 QRunnable 类型的对象
// 如果线程池中没有空闲的线程了, 任务会放到任务队列中, 等待线程处理
void QThreadPool::start(QRunnable * runnable, int priority = 0);
// 如果线程池中没有空闲的线程了, 直接返回值, 任务添加失败, 任务不会添加到任务队列中
bool QThreadPool::tryStart(QRunnable * runnable);

// 线程池中被激活的线程的个数(正在工作的线程个数)
int QThreadPool::activeThreadCount() const;

// 尝试性的将某一个任务从线程池的任务队列中删除, 如果任务已经开始执行就无法删除了
bool QThreadPool::tryTake(QRunnable *runnable);
// 将线程池中的任务队列里边没有开始处理的所有任务删除, 如果已经开始处理了就无法通过该函数删除了
void QThreadPool::clear();

// 在每个Qt应用程序中都有一个全局的线程池对象, 通过这个函数直接访问这个对象
static QThreadPool * QThreadPool::globalInstance();

一般情况下,我们不需要在 Qt 程序中创建线程池对象,直接使用 Qt 为每个应用程序提供的线程池全局对象即可。得到线程池对象之后,调用 start() 方法就可以将一个任务添加到线程池中,这个任务就可以被线程池内部的线程池处理掉了,使用线程池比自己创建线程的这种多种多线程方式更加简单和易于维护。

具体的使用方式如下:

mywork.h

class MyWork :public QRunnable
{
    Q_OBJECT
public:
    explicit MyWork();
    ~MyWork();

    void run() override;
}

mywork.cpp

MyWork::MyWork() : QRunnable()
{
    // 任务执行完毕,该对象自动销毁
    setAutoDelete(true);
}
void MyWork::run()
{
    // 业务处理代码
    ......
}

mainwindow.cpp

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

    // 线程池初始化,设置最大线程池数
    QThreadPool::globalInstance()->setMaxThreadCount(4);
    // 添加任务
    MyWork* task = new MyWork;
    QThreadPool::globalInstance()->start(task);    
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/697027.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第14章-Python-人工智能-语言识别-调用百度语音识别

百度语音识别API是可以免费试用的,通过百度账号登录到百度智能云,在语音技术页面创建的应用,生成一个语音识别的应用,这个应用会给你一个APIKey和一个Secret Key,如图14.1所示。 我们在自己的程序中用 API Key 和 Secr…

轻松搞定 Git

目录 前言 一、下载 二、安装 三、基本使用 四、git的基本原理 五、通过案例学习git 5.1 创建空的项目文件夹 5.2 初始化git 5.3 创建项目文件 5.4 查看git状态 5.5 添加到暂存区 5.6 提交到本地仓库 5.7 查看git提交到本地仓库的记录 5.8 .gitignore文件 六、分…

mysql基础2——增、删、改、查

文章目录 一、DDL操作1.1 数据库操作1.2 表操作1.3 用户操作1.4 查看命令show1.5 获取帮助 二、DCL操作2.1 用户授权2.2 查看授权2.3 取消授权 三、DML操作3.1 插入insert3.2 查询select3.2.1 常规查询3.2.2 条件查询3.2.3 order by用法3.2.4 group by用法3.2.5 内连接&左连…

输入年月日,日期; 求这个日期在这一年中是第几天

输入年月日,日期; 求这个日期在这一年中是第几天 1.问题 输入年月日,日期; 求这个日期在这一年中是第几天 2.代码 利用Java中的库函数 LocalDate 是Java 8引入的一个日期类,用于表示日期,不包含时间和时区信息 到时候直接调用方法可以获取对应的天数 p…

javaee ajax请求后台 不刷新页面

jsp页面 Reg.jsp <% page language"java" contentType"text/html; charsetUTF-8"pageEncoding"UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd&qu…

【Python】经过一个点P3的一条直线垂直于已知直线,求交点坐标

一个高中数学题目&#xff0c;已经点P1和点P2构成直线&#xff0c;经过P3点做已知直线的垂线&#xff0c;求垂线与已知直线的交点坐标。 p1 [100, 15] p2 [16, 85] p3 [-50, 100] if p2[0] - p1[0] 0:# p1 p2 构成垂线&#xff0c;那么垂直线就是一条水平线x p1[0]y p3…

kafka3.x 入门 常用命令(二)

创建主题 kafka-topics.sh --bootstrap-server hadoop100:9092 --create --partitions 1 --replication-factor 3 --topic first查看主题列表 kafka-topics.sh --bootstrap-server hadoop100:9092 --list查看主题详情 kafka-topics.sh --bootstrap-server hadoop100:9092 --…

element-ui—textarea多行输入框—字数限制及优化

属性作用 show-word-limit &#xff1a;是否显示数字显示 maxlength“300”&#xff1a;设置最大值 class“public-showWordLimit”&#xff1a; 优化数字的显示的位置 :autosize “{ minRows: 2, maxRows: 8 }” &#xff1a;根据输入字符长度设置动态高度 2.代码案例 <…

vue3+vite安装配置element-plus

配置 element-plus 1. 安装 yarn add element-plus element-plus/icons-vue2. 按需引入插件 yarn add unplugin-vue-components unplugin-auto-import -D3. 配置vite.config.ts // vite.config.ts import AutoImport from unplugin-auto-import/vite import Components fro…

MySQL数据库的主从复制与读写分离

MySQL数据库的主从复制与读写分离 一、主从复制原理1、MySQL支持主从复制类型2、主从复制的原理3、主从复制的架构4、mysql主从复制延迟4、slave从服务器的配置5、验证主从复制的效果6、从服务器的故障问题解决1、遇到Slave_IO_Running:NO的情况2、遇到Slave_SQL_Running&#…

C++ - 哈希的应用

前面的文章中我们讲解了如何进行哈希表的构建以及使用实现的哈希表来模拟实现unordered_map&#xff0c;在本文中我们将继续来讲解一下哈希的应用。 位图 问题引入 首先我们来引入一个问题&#xff1a;给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&am…

Pyside6-第十三篇-布局(最后一章废话-理论篇)

本篇Pyside6的第十三篇&#xff0c;新知识点&#xff0c;布局。 布局的方式有5种。着重挑选几种将 QVBoxLayout&#xff08;垂直布局&#xff09;&#xff1a;按垂直方向排列小部件。 QHBoxLayout&#xff08;水平布局&#xff09;&#xff1a;按水平方向排列小部件。 QGridLay…

关于函数和变量命名

标识符命名基本要求 标识符是指用来识别某个实体的一个符号&#xff0c;在不同的应用环境下有不同的含义。 在计算机编程语言中&#xff0c;标识符是用户编程时使用的名字&#xff0c;用于给变量、常量、函数、语句块等命名&#xff0c;以建立起名称与使用之间的关系。 C语言…

jdk代理和cglib代理(实例推导)

目录 jdk代理和cglib代理&#xff08;实例推导&#xff09;jdk动态代理Cglib动态代理总结 jdk代理和cglib代理&#xff08;实例推导&#xff09; 更深层的探究jdk和cglib动态代理的原理 jdk动态代理 jdk动态代理&#xff08;简单实现&#xff09; 定义一个House的房源类型接口…

05 2024考研408-计算机组成原理第五章-中央处理器学习笔记

文章目录 前言一、CPU的功能与基本结构1.1、CPU的功能1.2、运算器与控制器需要实现功能1.3、运算器的基本结构1.3.1、基本结构构成&#xff08;七个部分&#xff09;1.3.2、各个部件详细介绍①算数逻辑运算单元②通用寄存器组&#xff08;介绍数据通路的基本结构2个&#xff09…

Python教程(1)——python环境的下载与安装

Python教程(1)——python环境的下载与安装 下面是下载并安装Python解释器的具体步骤&#xff0c;非常详细&#xff0c;保姆级别的教程&#xff0c;初学者一步一步的按照操作。 下载python运行环境 访问官方网站 在浏览器中打开Python的官方网站&#xff0c;网址为 https://…

【PyTest】玩转HTML报告:修改、汉化和优化

前言 Pytest框架可以使用两种测试报告&#xff0c;其中一种就是使用pytest-html插件生成的测试报告&#xff0c;但是报告中有一些信息没有什么用途或者显示的不太好看&#xff0c;还有一些我们想要在报告中展示的信息却没有&#xff0c;最近又有人问我pytest-html生成的报告&a…

vue中由 window.open转为二进制流下载 遇到下载之后无法打开或乱码的坑 (responseType: ‘blob‘ 无效)

我项目中 request.js文件用的是 axios请求的. 如果使用 window.open 下载的话没有太多要求了,但是安全性不行. 如果使用 二进制流的话就需要设置: responseType: blob (设置请求返回类型) function exportData(orgId, personName, gender) {return request({url: /console/e…

时钟、时钟域

1.1 时钟 时钟信号是一个按一定电压幅度&#xff0c;一定时间间隔连续发出的脉冲信号。 脉冲信号之间的时间间隔称为周期&#xff1a;在单位时间内所产生的脉冲个数称为频率&#xff0c;频率的标准计量单位是Hz&#xff08;赫兹&#xff09; 每一次时钟脉冲到来&#xff0c;芯…

yolov8-03训练自己的数据集并保存推理结果

目标&#xff1a;将推理结果保存为xyxy形式&#xff0c;并以 pkl 格式保存 主要采取了两种方式&#xff0c;一种是阅读源码&#xff0c;通过CIL的方式保存结果。 一种是在IDE内&#xff0c;通过python代码的形式。 查看推理相关的源码&#xff0c;探索保存结果的相关信息。 在…