详细的线程池讲解,手写C与C++版本

在此感谢苏丙榅的教程讲的很详细,我看了他的C版本教程,对线程池有了深刻理解,手写了C版本,并自主改了C++版本。

线程池是消费者生产者模型的其中之一。这里面的线程城同步很重要,稍不注意就会造成死锁。主要用的是互斥锁mutex。学习线程池需要有线程的基础知识,比如线程创建,互斥锁,销毁线程,条件变量等等。接下来详细介绍线程池。

首先来看下图,下图所示了单进程单线程系统处理任务的方式,很多任务任务进来时,需要排队处理

下面让我们看进程池的处理方式,看下图:

在这里简单概括下线程池工作原理:

简单来说,任务进来后被添加到任务队列,当线程池里有空闲的线程时(阻塞态),该线程会被唤醒,尝试拿线程池的锁,拿到锁后的线程会从任务队列里取任务,任务队列任务少一,处理完任务后,若任务队列为空则再次进入阻塞态,当任务队列任务过多时,该条件自己设定,会增加存活的线程数量,但小于总的线程池大小,可以看出这是一个典型的消费者生产者模型。

线程池的重要组成部分:

线程池的重要组成部分
1、任务队列: 存放任务,一个
2、工作者线程: 尝试从任务队列取任务并处理,N个
3、管理者线程: 用来管理线程池的线程,方便扩容与缩小,一个。

接下来手写线程池:

任务结构体:

用来存放任务函数与任务函数参数:

C语言版本

//任务结构体
typedef struct Task
{void (*fun)(void * arg);void * arg;
}TASK;

C++版本

//任务结构体
struct Task
{void (*fun)(void * arg);void * arg;
};

线程池结构体/类:

用来定义线程池里所需要的东西:线程池大小,忙的线程,存活的线程,任务队列,队列大小,队列容量,管理者线程ID,工作线程ID,线程池销毁标志,两把锁(锁忙线程数,锁线程池),两个条件变量(任务队列空,任务队列满),等等。

C语言版本:

//线程池结构体
struct pthread_pool
{/* 任务对列*/TASK * task;int queueCapacity; //容量int queueSize;     //当前任务个数int queueFront;    //队头——>取数据int queueRear;     //队尾——>存数据//线程池相关pthread_t manmagerID; //管理者线程IDpthread_t *workerIDs;   //工作的线程IDint minNum;  //最小线程数int maxNum;  //最大线程数int busyNum; //忙碌线程数//busy单独加锁,因为经常修改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;};

C++版本:

//创建线程池类
class pthread_pool{private:int max_num{}; //线程池最大容量int min_num{}; //线程池最小容量int queueCapacity{};//任务队列容量std::queue<task> Task;//创建任务队列pthread_t manager{};//管理者线程IDstd::vector<pthread_t> workerIDs; //存储工作者线程IDsint shutdown{};//关闭线程池标志int busy_num{};//忙的线程数量int alive_num{};//活的线程数量int destory_or_add_num{}; //要销毁的线程数//两把锁,两个条件变量pthread_mutex_t mutexbusy{};pthread_mutex_t mutexpool{};pthread_cond_t  not_empty{};pthread_cond_t  notfull{};public:pthread_pool();pthread_pool(int min_num,int max_num,int queue_capacity);static void * pthread_manager(void * arg);static void * pthread_worker(void * arg);void pthread_add_work(pthread_pool * pool,void(*func)(void*),void * arg);int pthread_destory_pool(pthread_pool * pool);void pthreadExit(pthread_pool *pool);~pthread_pool();};

创建线程:

C语言版本:有点c++内容,把new换成malloc即可,nullptr换成NULL

pthread_pool * pthread_pool_create(int min,int max,int queueSize){pthread_pool * pool=new pthread_pool;if(pool==nullptr){printf("pool new is error");return nullptr;}do{pool->workerIDs = new pthread_t[max];if(pool->workerIDs==nullptr){printf("pool->workerIDs new is error");break;}memset(pool->workerIDs,0,sizeof(pool->workerIDs)); //这里有点不一样pool->minNum = min;pool->maxNum = max;pool->busyNum= 0;pool->liveNum= min;pool->exitNum= 0;//初始化两个锁,两个条件变量if(pthread_mutex_init(&pool->mutexpool,nullptr)!=0||pthread_mutex_init(&pool->mutexBusy,nullptr)!=0||pthread_cond_init(&pool->notEmpty,nullptr)!=0||pthread_cond_init(&pool->notFull,nullptr)!=0){printf("mutex or cond is error");break;}//任务队列pool->task = new TASK[queueSize];pool->queueCapacity = queueSize;pool->queueFront = 0;pool->queueRear = 0;pool->shutdown =0;//创建线程pthread_create(&pool->manmagerID,nullptr,manager,pool);for(int i = 0;i<min;++i){pthread_create(&pool->workerIDs[i],nullptr,worker,pool);}return pool;}while(0);if(pool&&pool->workerIDs) delete [] pool->workerIDs;if(pool&&pool->task) delete [] pool->task;if(pool) delete pool;return nullptr;
}

c++版本:

pthread_pool::pthread_pool(int min_num, int max_num,int queue_capacity) {this->workerIDs.insert(this->workerIDs.begin(),max_num,0);this->min_num=min_num;this->max_num=max_num;this->busy_num=0;this->alive_num=min_num;this->queueCapacity=queue_capacity;this->shutdown=0;this->destory_or_add_num=0;//创建管理者线程pthread_create(&this->manager, nullptr,pthread_manager, this);//传this指针//创建消费者线程for(auto &w_IDs:workerIDs){pthread_create(&w_IDs, nullptr,pthread_worker,this);//传this指针}//两把锁两个条件变量if(pthread_mutex_init(&this->mutexbusy, nullptr)!=0|| pthread_mutex_init(&this->mutexpool, nullptr)|| pthread_cond_init(&this->not_empty, nullptr)!=0|| pthread_cond_init(&notfull,nullptr)!=0){printf("construct is failed");exit(-1);}}

管理者线程:

当任务过多的时候,可以适当的创建一些新的工作线程
当任务过少的时候,可以适当的销毁一些工作的线程

C语言版本:

//管理者线程
void * manager(void * arg){pthread_pool * pool=(pthread_pool *) arg;while(!pool->shutdown){//每隔N秒检测一次sleep(N);//取出线程池中任务数量和当前线程数量pthread_mutex_lock(&pool->mutexpool);int queueSize = pool->queueSize;int liveNum   = pool->liveNum;pthread_mutex_unlock(&pool->mutexpool);//取出忙的线程的数量pthread_mutex_lock(&pool->mutexBusy);int busyNum = pool->busyNum;pthread_mutex_unlock(&pool->mutexBusy);//添加线程if(queueSize > liveNum&&liveNum<pool->maxNum){int count=0;pthread_mutex_lock(&pool->mutexpool);for(int i=0;i<pool->maxNum&&pool->liveNum<pool->maxNum&&count<create_or_destory_P;++i){if(pool->workerIDs[i]==0){count++;pthread_create(&pool->workerIDs[i],nullptr,worker,pool);pool->liveNum++;}}  pthread_mutex_unlock(&pool->mutexpool);         }//删除线程if(busyNum*2<liveNum&&liveNum>pool->minNum){int count=0;pthread_mutex_lock(&pool->mutexpool);pool->exitNum = create_or_destory_P;pthread_mutex_unlock(&pool->mutexpool);         //让线程自杀for(int i=0;i<create_or_destory_P;++i){pthread_cond_signal(&pool->notEmpty);}}}return nullptr;
}

c++版本

void * pthread_pool::pthread_manager(void * arg) {auto * pool= (pthread_pool *)arg;while(!pool->shutdown){sleep(N);//取出忙的线程数pthread_mutex_lock(&pool->mutexbusy);int busy=pool->busy_num;pthread_mutex_unlock(&pool->mutexbusy);pthread_mutex_lock(&pool->mutexpool);int queue_size=pool->Task.size();int live_num=pool->alive_num;pthread_mutex_unlock(&pool->mutexpool);if(queue_size>live_num&&pool->alive_num<pool->max_num){int count=0;pthread_mutex_lock(&pool->mutexpool);for(int i=0;i<pool->max_num&&count<d_or_a&&pool->alive_num<pool->max_num;++i){if(pool->workerIDs[i]==0){pthread_create(&pool->workerIDs[i], nullptr,pthread_worker,pool);count++;pool->alive_num++;}}pthread_mutex_unlock(&pool->mutexpool);}if(busy*2>live_num&&pool->alive_num>pool->min_num){pthread_mutex_lock(&pool->mutexpool);pool->destory_or_add_num=d_or_a;pthread_mutex_unlock(&pool->mutexpool);for(int i=0;i<d_or_a;++i){pthread_cond_signal(&pool->not_empty);}}}return nullptr;
}

工作线程:

线程池中维护了一定数量的工作线程,他们的作用是是不停的读任务队列,从里边取出任务并处理,作的线程相当于是任务队列的消费者角色,如果任务队列为空,工作的线程将会被阻塞 (使用条件变量 / 信号量阻塞)如果阻塞之后有了新的任务,由生产者将阻塞解除,工作线程开始工作。

C语言版本

//工作线程
void * worker(void *arg){printf("********************\n");pthread_pool * pool=(pthread_pool *) arg;while(1){pthread_mutex_lock(&pool->mutexpool);//当前任务队列是否为空while(pool->queueSize == 0 && !pool->shutdown){pthread_cond_wait(&pool->notEmpty,&pool->mutexpool);if(pool->exitNum>0){pool->exitNum--;if(pool->liveNum>pool->minNum){pool->liveNum--;pthread_mutex_unlock(&pool->mutexpool);pthreadExit(pool);}    }}//判断线程池是否关闭if(pool->shutdown){pthread_mutex_unlock(&pool->mutexpool); //为什么需要再次拿锁pthreadExit(pool);}//从任务队列取一个任务TASK _task;_task.fun = pool->task[pool->queueFront].fun;_task.arg = pool->task[pool->queueFront].arg;//移动头结点 环形队列pool->queueFront = (pool->queueFront+1) % pool->queueCapacity;pool->queueSize--;pthread_cond_signal(&pool->notFull);pthread_mutex_unlock(&pool->mutexpool);printf("start\n");//修改busynumpthread_mutex_lock(&pool->mutexBusy);pool->busyNum++;pthread_mutex_unlock(&pool->mutexBusy);_task.fun(_task.arg);delete _task.arg;_task.arg=nullptr;//(*_task.fun)(_task.arg);//修改busysumpthread_mutex_lock(&pool->mutexBusy);pool->busyNum--;pthread_mutex_unlock(&pool->mutexBusy);printf("end\n");}return nullptr;
}

C++版本

void * pthread_pool::pthread_worker(void *arg) {printf("***********\n");auto * pool= (pthread_pool *)arg;while(1){pthread_mutex_lock(&pool->mutexpool);while(!pool->shutdown && pool->Task.size()<=0){pthread_cond_wait(&pool->not_empty,&pool->mutexpool);if(pool->destory_or_add_num>0){pool->destory_or_add_num--;if(pool->alive_num>pool->min_num){pool->alive_num--;pthread_mutex_unlock(&pool->mutexpool);pool->pthreadExit(pool);}}}if(pool->shutdown){pthread_mutex_unlock(&pool->mutexpool);pool->pthreadExit(pool);}task _task;_task.fun=pool->Task.front().fun;_task.arg=pool->Task.front().arg;pool->Task.pop();pthread_cond_signal(&pool->notfull);pthread_mutex_unlock(&pool->mutexpool);pthread_mutex_lock(&pool->mutexbusy);pool->busy_num++;pthread_mutex_unlock(&pool->mutexbusy);pthread_mutex_lock(&pool->mutexpool);printf("start!!!!!!\n");_task.fun(_task.arg);//delete arg;printf("end!!!!!!\n");printf("***********\n");pthread_mutex_unlock(&pool->mutexpool);pthread_mutex_lock(&pool->mutexbusy);pool->busy_num--;pthread_mutex_unlock(&pool->mutexbusy);}return nullptr;
}

添加任务到任务队列函数

C语言版本

void pthreadpooladd(pthread_pool *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->task[pool->queueRear].fun=func;pool->task[pool->queueRear].arg=arg;pool->queueRear = (pool->queueRear+1)%pool->queueCapacity;pool->queueSize++;pthread_cond_signal(&pool->notEmpty);pthread_mutex_unlock(&pool->mutexpool);
}

C++版本

void pthread_pool::pthread_add_work(pthread_pool *pool,void(*func)(void*),void *arg) {pthread_mutex_lock(&pool->mutexpool);while(pool->Task.size()==pool->queueCapacity&&!pool->shutdown){pthread_cond_wait(&notfull,&pool->mutexpool);}if(pool->shutdown){pthread_mutex_unlock(&pool->mutexpool);return;}task _task;_task.fun=func;_task.arg=arg;pool->Task.push(_task);pthread_cond_signal(&pool->not_empty);pthread_mutex_unlock(&pool->mutexpool);
}

未来计划:把线程池加入《干饭聊天室》

在此感谢苏丙榅的教程;

详细的线程池讲解,手写C与C++版本相关推荐

  1. Java多线程之线程池的手写改造和拒绝策略

    Java多线程之线程池的手写改造和拒绝策略 目录 自定义线程池的使用 四种拒绝策略代码体现 1. 自定义线程池的使用 自定义线程池(拒绝策略默认AbortPolicy) public class My ...

  2. 太完整了!这是我见过最详细的线程池讲解了

    1. 前言 1.1 什么是线程池? 线程池是一种利用池化技术思想来实现的线程管理技术,主要是为了复用线程.便利地管理线程和任务.并将线程的创建和任务的执行解耦开来.我们可以创建线程池来复用已经创建的线 ...

  3. Android多线程:这是一份全面 详细的线程池(ThreadPool)讲解教程

    前言 对于多线程,大家应该很熟悉.但是,大家了解线程池吗? 今天,我将带大家全部学习关于线程池的所有知识. 目录 1. 简介 2. 工作原理 2.1 核心参数 线程池中有6个核心参数,具体如下 上述6 ...

  4. 神经网络学习(三)比较详细 卷积神经网络原理、手写字体识别(卷积网络实现)

    之前写了一篇基于minist数据集(手写数字0-9)的全连接层神经网络,识别率(85%)并不高,这段时间学习了一些卷积神经网络的知识又实践了一把, 识别率(96%左右)确实上来了 ,下面把我的学习过程 ...

  5. 超详细的线程池原理解析

    说明 线程池作为常用的并发工具重要性不言而喻,本文针对线程池进行了抽丝剥茧般的深入解析,希望大家看后会有帮助. 1 ThreadPoolExecutor关系 2 结构 public ThreadPoo ...

  6. java io密集型任务_Java线程池讲解——针对IO密集型任务

    sap java开发技术详解&mdash基础 94.01元 (需用券) 去购买 > 针对 IO 密集型的任务,我们可以针对原本的线程池做一些改造,从而可以提高任务的处理效率. 基本 在阿 ...

  7. 池化技术及jdk的线程池讲解

    概述 程序运行的本质是消耗系统资源,线程.数据库连接等都会耗费系统的资源.线程.数据库连接等的创建.销毁等都十分消耗系统资源,所以,如果使用池化技术(线程池.数据库连接池等),可以对系统资源进行控制和 ...

  8. opencv 多线程加速_线程池给你写好了,想加速拿来用就行哈

    图像拼接实现见: OpenCV源码系列|图像拼接1 OpenCV源码系列|图像拼接2 耗时在调用函数: Mat pano; Ptr stitcher = Stitcher::create(mode); ...

  9. KKB:线程池、四个线程池讲解

    线程池 Executors 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,我们就需要频繁的创建线程和销毁线程,降低系统的效率.线程池就是一个容纳多个线程的容器,池中的线程可以 ...

最新文章

  1. QT学习之图形视图框架
  2. 大话设计模式读书笔记--4.代理模式
  3. R语言基础练习与入门实践
  4. 将字符串转换为DateTime
  5. Blazor 路由及导航开发指南
  6. 活动丨PGConf.Asia大会11月17-20日线上直播!
  7. python接口自动化(十六)--参数关联接口后传(详解)
  8. php 获取 url 的操作 非常有用!
  9. python必备入门代码-python基础入门这一篇就够
  10. 安装计算机的更新每次更新失败,win7电脑自动更新失败怎么办,电脑自动更新失败解决方法...
  11. 2021年软考网络工程师备考资料
  12. java基础27 单例集合Collection及其常用方法
  13. 回车键的ASCII值
  14. 双向晶闸管,调压电路,开关电路,楼梯灯电路,光控路灯,无电弧接触器电路
  15. 解决了Microsoft Visual C++ Build Tools下载/解决Visual C++ 14.0 is required的问题
  16. springboot JWT Token 自动续期的解决方案
  17. ubuntu服务器安装可视化桌面(Gnome)
  18. 支持移动触摸设备的简洁js幻灯片插件
  19. 2017阿里实习生在线编程题
  20. 计算机技术对艺术设计的意义,解析数字艺术对艺术设计的影响论文

热门文章

  1. Perl 模块安装总结
  2. 10篇一作SCI博士的走心分享--宏组学研究之“道”
  3. mSystems:从铁载体窥根际菌群互作大局,可见一斑!
  4. 这是入门生信,学习生信分析思路和数据可视化的首选?
  5. 微生物相关网络构建教程:MENA, LSA, SparCC和CoNet
  6. Nature :全球表层土壤中微生物组的结构和功能
  7. Microbiome综述|植物内部微生物的相互作用
  8. 登录账号 npm_自定义npm 及问题整理
  9. cmt跟踪算法 matlab_“水上大疆”招聘——雷达算法工程师
  10. R语言is.na函数实战(删除、替换、统计、条件判断等)