当前位置: 首页 > news >正文

【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;//条件变量
};

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

相关文章:

  • 配置redis挂载
  • 使用docker以容器方式安装redis
  • 【论文+视频控制】23.08DragNUWA1.5:通过集成文本、图像和轨迹来进行视频生成中的细粒度控制 (24.01.08开源最新模型)
  • linux下vsc的自动切换输入法解决方案
  • 【数据库】第三章 MySQL库表操作
  • 【AI接口】语音版、文心一言大模型和AI绘图、图片检测API
  • php基础学习之变量
  • Python项目——计算器(PySide6+Pyinstaller)
  • 盖子的c++小课堂:第二十六讲:双向链表
  • JavaEE-微服务-Vuex
  • 浅谈拨测在网络安全中的应用
  • 最强生产力|卸载并重装Anaconda3
  • LeetCode.670. 最大交换
  • “深入理解RabbitMQ交换机的原理与应用“
  • mysql 导入数据 1273 - Unknown collation: ‘utf8mb4_0900_ai_ci‘
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • CODING 缺陷管理功能正式开始公测
  • E-HPC支持多队列管理和自动伸缩
  • IndexedDB
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • vue-loader 源码解析系列之 selector
  • 大整数乘法-表格法
  • 工程优化暨babel升级小记
  • 前端js -- this指向总结。
  • 前端性能优化--懒加载和预加载
  • 试着探索高并发下的系统架构面貌
  • 双管齐下,VMware的容器新战略
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  •  一套莫尔斯电报听写、翻译系统
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • 阿里云重庆大学大数据训练营落地分享
  • ​​快速排序(四)——挖坑法,前后指针法与非递归
  • ​LeetCode解法汇总518. 零钱兑换 II
  • # Apache SeaTunnel 究竟是什么?
  • (1)(1.9) MSP (version 4.2)
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (第二周)效能测试
  • (二)c52学习之旅-简单了解单片机
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • (没学懂,待填坑)【动态规划】数位动态规划
  • ***监测系统的构建(chkrootkit )
  • **python多态
  • .NET Core 2.1路线图
  • .NET MVC第三章、三种传值方式
  • .NET 设计模式初探
  • .NET 依赖注入和配置系统
  • .netcore 获取appsettings
  • .NET的微型Web框架 Nancy
  • .NET构架之我见
  • .NET框架
  • @configuration注解_2w字长文给你讲透了配置类为什么要添加 @Configuration注解
  • [@Controller]4 详解@ModelAttribute
  • [BSGS算法]纯水斐波那契数列