C++ 线程池
目录
一、线程池实现原理
二、定义线程池的结构
三、创建线程池实例
四、添加工作的线程的任务函数
五、管理者线程的任务函数
六、往线程池中添加任务
七、获取线程池工作的线程数量与活着的线程数量
八、线程池的销毁
一、线程池实现原理
线程池的组成主要分为3个部分。这三部分配合工作就可以得到一个完整的线程池:
1、任务队列,存储需要处理的任务。由工作的想程来处理这些任务
- 通过线程池提供的API函数,将一个待处理的任务添加到任务队列,或者从任务队列中删除
- 已处理的任务会被从任务队列中删除
- 线程池的使用者,也就是调用线程池函数往任务队列中添加任务的线程就是生产者线程
2、工作的线程 (任务队列任务的消费者),N个
- 线程池中维护了一定数量的工作线程,他们的作用是不停的读任务队列,从里边取出任务并处理
- 工作的线程相当于是任务队列的消费者角色
- 如果任务队列为空,工作的线程将会被阻塞(使用条件变量/信号量阻塞)
- 如果阻塞之后有了新的任务,由生产者将阻塞解除,工作线程开始工作
3、管理者线程(不处理任务队列中的任务),1个
- 它的任务是周期性的对任务队列中的任务数量以及处于忙状态的工作线程个数进行检测
——当任务过多的时候,可以适当的创建一些新的工作线程
——当任务过少的时候,可以适当的销毁一些工作的线程
二、定义线程池的结构
#include "threadpool.h"//任务结构体
typedef struct Task
{void (*function) (void* arg);void* arg;
}Task;//线程池结构体
struct ThreadPool
{//任务队列Task* taskQ;int queueCapacity; //容量int queueSize; //当前任务个数int queueFront; //队头->取数据int queueRear; //队尾->放数据pthread_t managerID; //管理者线程IDpthread_t *threadIDs; //工作的线程IDint minNum; //最小线程数量int maxNum; //最大线程数量int busyNum; //忙的线程的个数int liveNum; //存活的线程的个数int exitNum; //要销毁的线程个数pthread _mutex_t mutexPool; //锁整个的线程池pthread_mutex_t mutexBusy; //锁busyNum变量 pthread_cond_t notFull; //任务队列是不是满了pthread_cond_t notEmpty; //任务队列是不是空了int shutdown; //是不是要销毁线程池,销毁为1,不销毁为0
};
三、创建线程池实例
typedef struct ThreadPool ThreadPool;
ThreadPool* threadPoolCreate(int min, int max, int queueSize)
{ThreadPool* pool=(ThreadPool*)malloc(sizeof (ThreadPool));do{if (pool == NULL){printf ( "malloc threadpool fail ... \n");break;}pool->threadIDs = (pthread_t *) malloc(sizeof(pthread_t) *max);if(pool->threadIDs == NULL){printf ("malloc threadIDs fail ... \n");break;}memset(pool->threadIDs,0, sizeof (pthread_t) * max);pool->minNum = min;pool->maxNum = max;pool->busyNum = 0;pool->liveNum = min; //和最小个数相等pool->exitNum = 0;if (pt.hread _mutex_init ( &pool->mutexPool,NUTI) !=0 ||pthread_mutex_init ( &pool->mutexBusy,NULL) !=0 ||pthread_cond_init (&pool->notEmpty,NULL) !=0 ||pthread_cond_init ( &pool->notFull,NULL) !=0 ){ printf ( "mutex or condition init fail ...\n");break;}//任务队列pool->taskQ = malloc(sizeof (Task) * queueSize);pool->queueCapacity =qucuesizo;pool->queueSize= 0;pool->queueFront =0;pool->queueRear = 0;pool->shutdown = 0;//创建线程pthread_create ( &pool->managerID,NULL,manager,NULL);for (int i = 0; i < min; ++i){pthread create (&pool->threadIDs[i],NULL,worker,NULL);}}whiie (O);//释放资源if(pool && pool->threadIDs) free(pool->threadIDs);if(pool && pool->taskQ) free(pool->taskQ);if(pool) free(pool);return NULL;
}
四、添加工作的线程的任务函数
void* worker (void* arg)
{ThreadPool* pool = (ThreadPool*)arg;while (1){pthread_ mutex_lock(&pool->mtexPool) ;!当前任务队列是否为空while (pool->queuesize == 0 && !pool->shutdown ){//阻塞工作线程pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);//判断是不是要销毁线程if (pool->exitNum > 0){pool->exitNum--;pthread_ mutex_unlock(&pool->mtexPool) ;!当前任务队列是否为空pthread_exit (NULL);}}//判断线程池是否被关闭了if (pool->shutdown ){pthread_mutex_unlock(&pool->mutexPool);pthread_exit(NULL);}//从任务队列中取出一个任务Task task;task.function = pool->taskQ[pool->queueFront].function;task.arg = pool->taskQ[pool->queueFront].arg;//移动头结点pool->queueFront =(pool->queueFront + 1) % pool->queueCapacity;pool->queuesize--;//解锁pthread_cond_signal(&pool->notFull);pthread_mutex_unlock(&pool->mutexPool);printf("thread %ld start working ...\n");pthread mutex_lock (&pool->mutexBusy);pool->busyNum++;pthread mutex_unlock (&pool->mutexBusy) ;task.function(task.arg) ; //或者 (*task.function) (task.arg) 进行调用;free(task.arg);task.arg=NULL;printf("thread %ld end working ...\n");pthread_mutex_lock (&pool->mutexBusy);pool->busyNum--;pthread mutex_unlock (&pool->mutexBusy) ; }return NULL;
}
五、管理者线程的任务函数
const int NUMBER = 2;
void* manaqer(void* arg)
{ThreadPool* pool = (ThreadPool+)arg;while(!pool->shutdown){//每隔3s检测一次sleep(3);//取出线程池中任务的数量和当前线程的数量pthread_mutex_lock(&pool->muatexPool) ;int queueSize =pool->queuesize;int liveNum = pool->liveNum;pthread_mutex_unlock( &pool->mutexPool) ;//取出忙的线程的数量pthread_mutex_lock(&pool->mutexBusy);int busyNum = pool->busyNum;pthread_mutex__lock(&pool->mutexBusy) ;//添加线程// 任务的个数 > 存活的线程个数 && 存活的线程数 < 最大线程数if (queueSize > liveNum && liveNum< pool->maxNum){pthread_mutex_lock(&pool->mutexPool);int counter = 0;for (int i = 0; i < pool->maxNum && counter < NUMBER && pool->liveNum < pool->maxNum ; ++i){if (pool->threadIDs[i] == 0){pthread_create( &pool->threadIDs[i],NULI,worker, pool);counter++;pool->liveNum++;}}pthread_mutex_unlock(&pool->mutexPool);}// 销毁线程// 忙的线程*2 < 存活的线程数 && 存活的线程 > 最小线程数if(busyNum * 2< liveNum && liveNum > pool->minNum){pthread_mutex_lock(&pool->mutexPool);pool->exitNum = NUMBER;pthread_mutex_unlock(&pcol->mutexPool);// 让工作的线程自杀Ifor(int i=0;i<NUMBER; ++i){pthread_cond_signal(&pool->notEmpty);}}}
}
六、往线程池中添加任务
void threadPoolAdd(ThreadPool* pool,void (*func)(void*), void* arg)
{pthread_mutex_lock(&pool->mutexPool);while(pool->queueSize == pool->queueCapacity && !pool->shutdown){//阻塞生产者线程pthread_cond_wait( &pool->notFull,&pool->mutexPool) ;}if (pool->shutdown){pthread_mutex_unlock ( &pool->mutexPool) ;return;}//添加任务pool->taskQ[pool->queueRear].function = func;pool->taskQ[pool->queueRear].arg = arg;pool->queueRear = (pool->queueRear + 1) % pool->queueCapacity;pool->queuesize++;pthread_cond_signal( &pool->notEmpty);pthread_mutex_unlock(&pool->mutexPool) ;
}
七、获取线程池工作的线程数量与活着的线程数量
//获取线程池中工作的线程数量
int threadPoolBusyNum ( ThreadPool* pool)
{pthread_mutex_lock(&pool->mutexBusy);int busyNum = pool->busyNum;pthread_mutex_unlock(&pool->mutexBusy);return busyNum;
}//获取线程池中活着的线程数量
int threadPoolAliveNum (ThreadPool*pool)
{pthread_mutex_lock(&pool->mutexPool);int busyNum = pool->liveNum;pthread_mutex_unlock(&pool->mutexPool);return aliveNum;
}
八、线程池的销毁
int threadPoolDestroy (ThreadPool* pool)
{if(pool == NULL){return -1;}//关闭线程池pool->shutdown = 1;//阻塞回收管理者线程pthread_join(pool->nanagerID,NULL);//唤醒阻塞的消费者线程for (int i = 0; i < pool->liveNum; ++i){pthread_cond_signal( &pool->notEmpty) ;}//释放堆内存if(pool->taskQ){free(pool->taskQ);pool->taskQ=NULL;}if(pool->threadIDs){free(pool->threadIDs);pool->threadIDs=NULL;}free(pool);pool=NULL;pthread_mutex_destroy( &pool->mutexPool);pthread_mutex_destroy( &pool->mutexBusy);pthread_cond_ destroy( &pool->notEmpty);pthread_cond_destroy ( &pool->notFull);return 0;
}
相关文章:
C++ 线程池
目录 一、线程池实现原理 二、定义线程池的结构 三、创建线程池实例 四、添加工作的线程的任务函数 五、管理者线程的任务函数 六、往线程池中添加任务 七、获取线程池工作的线程数量与活着的线程数量 八、线程池的销毁 一、线程池实现原理 线程池的组成主要分为3个部…...

测试框架pytest教程(6)钩子函数hook开发pytest插件
pytest hook 函数也叫钩子函数,pytest 提供了大量的钩子函数,可以在用例的不同生命周期自动调用。 比如,在测试用例收集阶段,可利用 hook 函数修改测试用例名称的编码。 pytest的hook是基于Python的插件系统实现的,使…...

【Rust】Rust学习 第十七章Rust 的面向对象特性
面向对象编程(Object-Oriented Programming,OOP)是一种模式化编程方式。对象(Object)来源于 20 世纪 60 年代的 Simula 编程语言。这些对象影响了 Alan Kay 的编程架构中对象之间的消息传递。他在 1967 年创造了 面向对…...

Redis系列(四):哨兵机制详解
首发博客地址 https://blog.zysicyj.top/ 前面我们说过,redis采用了读写分离的方式实现高可靠。后面我们说了,为了防止主节点压力过大,优化成了主-从-从模式 思考一个问题,主节点此时挂了怎么办 这里主从模式下涉及到的几个问题&a…...
一个滚动框高度动态计算解决方案
需求描述,一个嵌套了很多层div或者其他标签的内容框,而它的外层没有设置高度,或者使用百分比,而本容器需要设置高度来实现滚动,要么写死px高度,但是不能自适应,此时需要一个直系父容器ÿ…...
Android瀑布流
以下是一个简单的示例代码,演示如何在Android Studio中解析指定网页的图片URL,并展示在错乱瀑布流布局中: 1. 添加网络权限:在项目的AndroidManifest.xml文件中添加以下权限: <uses-permission android:name"…...

Ubuntu搭建CT_ICP里程计的环境暨CT-ICP部署
CT-ICP部署以及运行复现过程 0.下载资源,并按照github原网址的过程进行。1.查看所需要的各个部分的版本。2.安装clang编译器3.进行超级构建3.1标准进行3.2构建过程中遇到的问题 4.构建并安装CT-ICP库4.1标准进行4.2遇到的问题及解决办法 5.构建 CT-ICP 的 ROS 包装5…...
微信小程序全局事件订阅eventBus
微信小程序全局事件订阅 在Vue开发中,我们可能用过eventBus来解决全局范围内的事件订阅及触发逻辑,在微信小程序的开发中我们可能也也会遇到同样的需求,那么我们尝试下在小程序(原生小程序开发)中实现类似eventBus的事…...
华为云cce发布若依前后分离版:2.nginx镜像操作
下载nginx docker的官方镜像。 docker资源很难找,我在我的空间上传了一个,需要的话可以下载: https://download.csdn.net/download/axe6404/88225311 下载后,请用以下方法安装 2.1 导入docker 官方nginx镜像。 将镜像包nginx docker镜像包nginx-dockerimage.tar放…...

TCP协议报文结构
TCP是什么 TCP(传输控制协议)是一种面向连接的、可靠的、全双工的传输协议。它使用头部(Header)和数据(Data)来组织数据包,确保数据的可靠传输和按序传递。 TCP协议报文结构 下面详细阐述TCP…...

Day14-2-NodeJS后端开发流程
Day14-NodeJS后端工程化流程 一 apifox工具 apifox是目前最好的接口调试工具 1 环境搭建 安装登录创建项目接口里面创建对应文件夹在指定的文件夹里面创建接口2 GET请求 1 apifox发送GET请求 2 后端接收GET请求 router.get("/getUserinfo"...

计算机竞赛 基于CNN实现谣言检测 - python 深度学习 机器学习
文章目录 1 前言1.1 背景 2 数据集3 实现过程4 CNN网络实现5 模型训练部分6 模型评估7 预测结果8 最后 1 前言 🔥 优质竞赛项目系列,今天要分享的是 基于CNN实现谣言检测 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐&am…...

框架(Git基础详解及Git在idea中集成步骤)
目录 基础: idea集成Git并添加项目到git仓库 1.idea集成git,集成.git.exe文件 2.初始化本地Git仓库项目 3. 将工作区代码添加到暂存区 4.将暂存区代码添加到本地仓库 5.Git本地库操作 Idea集成Gitee并提交代码到第三方库 1.setting里搜索gitee 2.添…...

0基础学习VR全景平台篇 第88篇:智慧眼-成员管理
一、功能说明 成员管理,是指管理智慧眼项目的成员,拥有相关权限的人可以进行添加成员、分配成员角色、设置成员分类、修改成员以及删除成员五项操作。但是仅限于管理自己的下级成员,上级成员无权管理。 二、前台操作页面 登录智慧眼后台操…...

DSO 系列文章(2)——DSO点帧管理策略
文章目录 1.点所构成的残差Residual的管理1.1.前端残差的状态1.2.后端点的残差的状态1.3.点的某个残差的删除 2.点Point的管理2.1.如何删除点——点Point的删除2.2.边缘化时删除哪些点? 3.帧FrameHessian的管理 DSO代码注释:https://github.com/Cc19245/…...

无需公网IP——搭建web站点
文章目录 概述使用 Raspberry Pi Imager 安装 Raspberry Pi OS设置 Apache Web 服务器测试 web 站点安装静态样例站点将web站点发布到公网安装 Cpolar内网穿透cpolar进行token认证生成cpolar随机域名网址生成cpolar二级子域名将参数保存到cpolar配置文件中测试修改后配置文件配…...
swift 项目集成友盟推送
1, 需要用桥接文件 , 不然引用不到依赖库 2, 可以用测试模式测试, 可以debug 3, 测试模式获取deviceToken, 添加测试设备 deviceToken获取方法 func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { le…...

Unity之用Transform 数组加多个空物体-->简单地控制物体按照指定路线自动行驶
文章目录 **原理解释**:**带注释的代码**:实际运用 当你需要实现物体按照指定路线行驶时,你可以通过以下步骤来实现: 原理解释: 路径点:你需要定义一系列路径点,这些点将构成物体行驶的路线。每…...

交换机生成树STP
生成树协议(spanning-tree-protocol,stp):在具有物理环路的交换机网络上生成没有回路的逻辑网络的方法,生成树协议使用生成树算法,在一个具有冗余路径的容错网络中计算出一个无环路的路径,使一部分端口处于…...

3.微服务概述
1.大型网络架构变迁 SOA与微服务最大的差别就是服务拆分的细度,目前大多数微服务实际上是SOA架构,真正的微服务应该是一个接口对应一个服务器,开发速度快、成本高; 微服务SOA能拆分的就拆分是整体的,服务能放一起的都…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...