【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,一经查实,立即删除!

相关文章

Distributed Transactions at Scale in Amazon DynamoDB——论文泛读

ATC 2023 Paper 论文阅读笔记整理 问题 NoSQL云数据库服务因其简单的键值操作、高可用性、高可扩展性和可预测的性能而广受欢迎。尽管NoSQL数据库很实用&#xff0c;但它们通常不支持事务。 键值存储等NoSQL数据库是关系数据库的一种替代方案&#xff0c;它强调可扩展性和性…

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

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

FFmpeg教程:libswscale对图像进行简单处理

我们在FFmpeg简单总结对FFmpeg 组成模块&#xff0c;编码进行了简单介绍。 FFmpeg组成部分&#xff1a; libavcodec&#xff1a; 提供了音视频编解码器的库。 libavformat&#xff1a; 处理多媒体容器格式的库&#xff0c;包括封装和解封装。 libavutil&#xff1a; 包含一些公…

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;了解从何处或如何开…

c++类与对象(五):友元、内部类、临时对象、匿名对象

上次重新再次补全了构造函数的内容&#xff0c;以及static成员&#xff1a;C类与对象&#xff08;四&#xff09;&#xff1a;再谈构造函数&#xff08;详解初始化列表&#xff09;、Static成员 今天就来进行类与对象最后一部分的内容 文章目录 1.友元1.1友元函数1.2友元类 2.内…

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;…

Python os模块

简介 Python的os模块是一个标准库模块&#xff0c;用于提供与操作系统相关的功能&#xff08;相当于接口&#xff09;。os模块允许Python程序与文件系统、目录结构、进程管理等操作系统级别的功能进行交互。 主要功能 文件和目录操作 创建、删除、重命名文件和目录&#xf…

centos7安装pip

centos7 没有python-pip包就执行命令 yum -y install epel-release 执行成功之后&#xff0c;再次执行 yum install python-pip 对安装好的pip进行升级 pip install --upgrade pip 至此&#xff0c;pip工具就安装好了。 所有的问题都是最后一刻解决&#xff0c;如果没有解决…

智慧工业园区建设方案-智慧化工园区物联网管理系统平台---豌豆云

将化工园区海量信息互联、互通、互融&#xff0c;结合化工园区建设管理经验&#xff0c;通过“动态感知、主动监测、政企联动、综合管理”。 将事件管理从事后处置变为事前防控&#xff0c;保障生产安全&#xff0c;提升环保水平&#xff0c;结合可视化大屏&#xff0c;帮助管…

静态路由实验

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

Vue.js代码检查

一、CSS 1、属性可安全的替换为速记形式 .el-dialog .el-dialog__header .el-dialog__body {padding: 0 40px;padding-top: 4px;}.el-dialog .el-dialog__header .el-dialog__body {padding: 4px 40px 0;} 2、度量单位冗余 padding: 0px 20px;// 修改后 padding: 0 20px; 3、…

过滤器监听器拦截器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…