【Linux】:线程池(逐行解析代码)

线程池

  • 一.概念
  • 二.模拟实现一个线程池

一.概念

一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

在这里插入图片描述

线程池的应用场景:

  1. 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
  2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
  3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。

线程池示例:

  1. 创建固定数量线程池,循环从任务队列中获取任务对象,
  2. 获取到任务对象后,执行任务对象中的任务接口

二.模拟实现一个线程池

1.我们要写一个线程池类(ThreadPool),首先要写它的构造和析构,很明显是多个线程,那么需要线程的个数(num),同时保证它们的互斥关系,需要锁(mutex)和条件变量(cond)。

#include<iostream>
#include<pthread.h>
#include<vector>
#include<string>
#include<queue>//每个线程的基本信息
struct ThreadInfo
{pthread_t tid;std::string name;
};//。默认线程池里有5个线程
static const int defalutnum = 5;template<class T>//由于不知道任务类型,用T代替
class ThreadPool
{
public:ThreadPool(int num=defalutnum): threads_(num){pthread_mutex_init(&mutex,nullptr);//初始化锁pthread_cond_init(&cond,nullptr);//初始化条件变量}~ThreadPool(){pthread_mutex_destroy(&mutex);//销毁锁pthread_cond_destroy(&cond);//销毁条件变量}private:std::vector<ThreadInfo> threads_;///线程池std::queue<T> tasks_;//任务pthread_mutex_t mutex;//锁pthread_cond_t cond;//条件变量
};

2.开始创建线程,给外部留一个Start接口,方便进行使用。

在这里插入图片描述

但这里有一个问题:当编译时会发生报错,因为HandlerTask属于ThreadPool的内置函数,所以第一个参数是隐藏的this指针,而线程函数的格式要求参数有且只有一个是void*类型。所以要么将它放在类外,要么就加static。

在这里插入图片描述

3.写一个Push函数用来添加任务。同时对上锁,解锁,休眠…进行封装以方便使用。

#include<iostream>
#include<pthread.h>
#include<vector>
#include<string>
#include<queue>//每个线程的基本信息
struct ThreadInfo
{pthread_t tid;std::string name;
};//。默认线程池里有5个线程
static const int defalutnum = 5;template<class T>//由于不知道任务类型,用T代替
class ThreadPool
{
private:void Lock()//上锁{pthread_mutex_lock(&mutex);}void Unlock()//解锁{pthread_mutex_unlock(&mutex);}void Wakeup()//唤醒{pthread_cond_signal(&cond);}void Threadsleep()//休眠{pthread_cond_wait(&cond,&mutex);}
public:ThreadPool(int num=defalutnum): threads_(num){pthread_mutex_init(&mutex,nullptr);//初始化锁pthread_cond_init(&cond,nullptr);//初始化条件变量}static void* HandlerTask(void* arges){}void Start(){int num=threads_.size();for(int i=0;i<num;i++){threads_[i].name="thread-"+std::to_string(i);//把线程名写入pthread_create(&(threads_[i].tid),nullptr,HandlerTask,nullptr);//创建线程}}void Push(const T&t)//向任务表里添加任务{//保证互斥要上锁Lock();tasks_.push(t);Wakeup();//唤醒一个线程执行任务Unlock();}~ThreadPool(){pthread_mutex_destroy(&mutex);//销毁锁pthread_cond_destroy(&cond);//销毁条件变量}private:std::vector<ThreadInfo> threads_;//线程池std::queue<T> tasks_;//任务pthread_mutex_t mutex;//锁pthread_cond_t cond;//条件变量
};

4.接下来就是多个线程如何执行任务了。

在这里插入图片描述

问题:这里编译会直接报错,因为HandlerTask是静态函数,不能直接使用成员变量。解决方法:传this指针。同时前面的方法是私有的,也需要改成公有的。

#include<iostream>
#include<pthread.h>
#include<vector>
#include<string>
#include<queue>//每个线程的基本信息
struct ThreadInfo
{pthread_t tid;std::string name;
};//。默认线程池里有5个线程
static const int defalutnum = 5;template<class T>//由于不知道任务类型,用T代替
class ThreadPool
{
public:void Lock()//上锁{pthread_mutex_lock(&mutex);}void Unlock()//解锁{pthread_mutex_unlock(&mutex);}void Wakeup()//唤醒{pthread_cond_signal(&cond);}void Threadsleep()//休眠{pthread_cond_wait(&cond,&mutex);}bool Isempty(){return tasks_.empty();}
public:ThreadPool(int num=defalutnum): threads_(num){pthread_mutex_init(&mutex,nullptr);//初始化锁pthread_cond_init(&cond,nullptr);//初始化条件变量}static void* HandlerTask(void* arges){ThreadPool<T>* tp=static_cast<ThreadPool<T>*>args;while(true){//先上锁tp->Lock();while(tp->Isempty())//使用while防止误唤醒{Threadsleep();//如果没有任务先休眠}T t=tp->Pop();//封装一个Pop函数tp->Unlock();//解锁//执行任务//t();//这里我写的样例任务是重载了(),所以直接用括号表示执行任务(大家可以自己任意写任务)}}T Pop(){T t=tasks_.front();tasks_.pop();return t;}void Start(){int num=threads_.size();for(int i=0;i<num;i++){threads_[i].name="thread-"+std::to_string(i);//把线程名写入pthread_create(&(threads_[i].tid),nullptr,HandlerTask,this);//创建线程}}void Push(const T&t)//向任务表里添加任务{//保证互斥要上锁Lock();tasks_.push(t);Wakeup();//唤醒一个线程执行任务Unlock();}~ThreadPool(){pthread_mutex_destroy(&mutex);//销毁锁pthread_cond_destroy(&cond);//销毁条件变量}private:std::vector<ThreadInfo> threads_;//线程池std::queue<T> tasks_;//任务pthread_mutex_t mutex;//锁pthread_cond_t cond;//条件变量
};

整个线程池已经简单的写完了,大家可以自己写一些任务来进行测试啦。

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

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

相关文章

博捷芯划片机在半导体芯片切割领域的领先实力

在当今高速发展的半导体行业中&#xff0c;芯片切割作为制造过程中的核心技术环节&#xff0c;对设备的性能和精度要求日益提升。在这方面&#xff0c;国内知名划片机企业博捷芯凭借其卓越的技术实力和持续的创新精神&#xff0c;成功研发出具备完全自主知识产权的半导体切割划…

MySQL 8.3 发布,具体有哪些新增和删减?

MySQL 8.3 主要更新&#xff1a;用于标记事务分组的 GTID、JSON EXPLAIN 格式增强、一些功能删除等。 MySQL 是一款广泛使用的开源的关系型数据库管理系统&#xff0c;已推出其最新版本 MySQL 8.3。它带来了新功能和一些删除&#xff0c;有望简化数据库操作。让我们来看看有哪些…

基本的 Socket 模型

什么是Socket Socket 的中文名叫作插口&#xff0c;咋一看还挺迷惑的。事实上&#xff0c;双方要进行网络通信前&#xff0c;各自得创建一个 Socket&#xff0c;这相当于客户端和服务器都开了一个“口子”&#xff0c;双方读取和发送数据的时候&#xff0c;都通过这个“口子”…

用通俗易懂的方式讲解:太棒了!构建大模型 Advanced RAG(检索增强生成)的速查表和实战技巧最全总结来了!

新的一年开始了&#xff0c;也许您正打算通过构建自己的第一个RAG系统进入RAG领域。或者&#xff0c;您可能已经构建了基本的RAG系统&#xff0c;现在希望将它们改进为更高级的系统&#xff0c;以更好地处理用户的查询和数据结构。 无论哪种情况&#xff0c;了解从何处或如何开…

JVM系列-4.类加载器

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术、JVM原理&#x1f525;如果感觉博主的文…

LeetCode670.最大交换

我真的怀疑他是不是难度等级评错了&#xff0c;因为感觉没到中级&#xff0c;总之先看题吧 给定一个非负整数&#xff0c;你至多可以交换一次数字中的任意两位。返回你能得到的最大值。 示例 1 : 输入: 2736 输出: 7236 解释: 交换数字2和数字7。示例 2 : 输入: 9973 输出:…

常用的三维尺寸公差分析软件有哪些?各有什么特点?

公差分析软件主要用于产品设计和制造过程中&#xff0c;帮助工程师们评估和控制产品的尺寸和公差。以下是一些常用的公差分析软件&#xff1a; 1.DTAS3D是一种用于三维尺寸公差分析的软件系统。 DTAS软件可以帮助工程师和设计师对零件和装配体的尺寸公差进行分析&#xff0c;…

静态路由实验

一&#xff1a;实验内容 二&#xff1a;实验分析 &#xff08;一&#xff09;&#xff1a;实验要求 1、R6为ISP&#xff0c;接口IP地址均为公有地址&#xff1b;该设备只能配置IP地址&#xff0c;之后不能再对其进行其他任何配置&#xff1b; 2、R1-R5为局域网&#xff0c…

过滤器监听器拦截器AOP

过滤器、监听器、拦截器、AOP的实现 一、过滤器 Filter ​ 在传统的Servlet容器中&#xff0c;可以使用过滤器和监听器&#xff0c;在Java框架中还可以使用拦截器。 ​ 过滤器&#xff0c;这里指的是Servlet过滤器&#xff0c;它是在Java Servlet中定义的&#xff0c;能够对…

EasyExcelFactory 导入导出功能的实战使用

EasyExcelFactory 导入导出功能的实战使用分享&#xff1a; 1、jar包引入 <!-- 阿里巴巴Excel处理--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.6</version></dependen…

1、Seaborn可视化库

你的数据可视化编程初体验! Seaborn是一个基于matplotlib的图形可视化Python库,它提供了一种高级的API接口,使得制作统计图形更加容易。 Seaborn的目标是使可视化成为探索和理解数据的核心部分,它面向数据集的绘图功能对整个数据集进行操作,并在内部执行必要的语义映射和统…

Python - 【Socket】消息粘包处理demo(一)

一. 前言 在网络编程中&#xff0c;粘包是指TCP协议在传输过程中&#xff0c;多条数据被合并成一条数据发送或者一条数据被拆分成多个数据发送的现象。 二. 粘包问题的常规处理方法&#xff1a; 使用固定长度的包头 可以在发送数据前先发送一个固定长度的包头&#xff0c;包…

个人云服务器docker搭建部署前后端应用-myos

var code "87c5235c-b551-45bb-a5e4-9593cb104663" mysql、redis、nginx、java应用、前端应用部署 本文以单台云服务器为例&#xff1a; 1. 使用腾讯云服务器 阿里或其他云服务器皆可&#xff0c;类似 安装系统&#xff0c;现在服务器系统都集成安装了docker镜像&a…

[ACM学习]自上而下树形dp

问题引入 设置dp状态&#xff0c;相比于更容易出错的贪心更...不易出错。 状态设计 如果选择父结点&#xff0c;就会使孩子结点不能被选择&#xff0c;我们会多开一维的dp&#xff0c;用来标记该点是否被标记过。 以1点举例&#xff0c;f[1][0]为不选它的状态&#xff0c;那么…

FOR XML PATH 函数与同一分组下的字符串拼接

FOR XML PATH 简单介绍 FOR XML PATH 语句是将查询结果集以XML形式展现&#xff0c;通常情况下最常见的用法就是将多行的结果&#xff0c;拼接展示在同一行。 首先新建一张测试表并插入数据&#xff1a; CREATE TABLE #Test (Name varchar(70),Hobby varchar(70) );insert #T…

芯驰E3340软件编译以及更新步骤

打开已有工程File->Open Solution: 东南项目&#xff1a;e3340\boards\e3_324_ref_display\proj\jetour-t1n-fl3\sf\SES 编译&#xff1a;build->build sf 增加头文件和宏定义&#xff1a; 编译完成sf后&#xff0c;进行编译bootloader 东南项目&#xff1a;e3340\boa…

IaC基础设施即代码:Terraform 创建ACK集群 与部署应用

目录 一、实验 1.环境 2.Terraform 创建网络资源 3. 阿里云给RAM添加权限 4.Terraform 创建 ACK集群 5.在ACK集群中部署应用 6.销毁资源 二、问题 1.Terraform 验证失败 2.Terraform申请资源失败 一、实验 1.环境 &#xff08;1&#xff09;主机 表1-1 主机 主机系…

火山引擎ByteHouse:“专用向量数据库”与“数据库+向量扩展”,怎么选?

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 背景 随着LLM&#xff08;Large Language Model&#xff09;的不断发展&#xff0c;向量检索也逐渐成为关注的焦点。LLM通过处理大量的文本数据&#xff0c;获取丰…

第1章-计算机网络基础

目录 1. 计算机网络与计算机 2. 计算机网络的定义和基本功能 2.1. 定义&#xff1a;计算机网络是一组自治计算机互连的集合 2.2. 基本功能 2.3. 计算机网络的演进 2.4. 广域网(Wide Area Network&#xff0c;WAN) 2.5. 网络的拓扑结构 2.6. 数据交换方式 2.7. 衡量计算…

图灵日记之java奇妙历险记--异常包装类泛型

目录 异常概念与体系结构异常的分类异常的处理防御式编程异常的抛出异常的捕获异常声明throwstry-catch捕获并处理 自定义异常类 包装类基本数据类型及其对应包装类装箱和拆箱 泛型泛型使用类型推导 裸类型说明 泛型的编译机制泛型的上界语法 异常概念与体系结构 在java中,将程…