当前位置:   article > 正文

线程池的工作原理及C++实现

线程池的工作原理及C++实现

本文的笔记来自于b站视频的爱编程的大丙,博客链接:https://subingwen.cn/,有做了相应的补充!

 一、线程池的原理

线程池是一种多线程处理的形式,处理过程中将任务添加到队列,任何在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件的发生),则线程池将插入另外一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

线程池的组成主要分为 3 个部分,这三部分配合工作就可以得到一个完整的线程池:

  • 任务队列,存储需要处理的任务,由工作的线程来处理这些任务

(1)通过线程池提供的API函数,将一个待处理的任务添加到任务队列,或者从任务队列中删除;

(2)已处理的任务将被删除;

(3)线程池的使用者,也就是调用线程池函数往任务队列中添加任务的线程就是生产者线程。

  • 工作线程(任务队列中任务的消费者),N个

(1)线程池维护了一定数量的工作线程,它们的作用是不断的读取任务队列,从里边取出任务并处理;

(2)如果任务队列为空,工作线程将会被阻塞(使用条件变量或信号量阻塞);

(3)如果阻塞之后有了新的任务,由生产者将阻塞解除,工作线程开始工作;

  • 管理者线程(不处理任务队列中的任务),1个

(1)它的任务是周期性的对任务队列中的任务数量以及处于忙状态的工作线程个数进行检测;

(2)当任务过多的时候,可以适当的创建一些新的工作线程;

(3)当任务过少的时候,可以适当的销毁一些工作线程;

1. 任务队列

(1)类的声明

  1. #pragma once
  2. #include <queue>
  3. #include <pthread.h>
  4. using callback=void(*)(void* arg);
  5. //任务结构体
  6. template<typename T>
  7. struct Task
  8. {
  9. Task()
  10. {
  11. function=nullptr;
  12. arg=nullptr;
  13. }
  14. Task(callback f,void* arg)
  15. {
  16. function=f;
  17. this->arg=(T*)arg;
  18. }
  19. callback function;
  20. T* arg;
  21. };
  22. template<typename T>
  23. //任务队列
  24. class TaskQueue
  25. {
  26. public:
  27. TaskQueue();
  28. ~TaskQueue();
  29. //添加一个任务
  30. void addTask(Task<T> task);
  31. void addTask(callback f,void* arg);
  32. //取出一个任务
  33. Task<T> taskTask();
  34. //获取当前任务个数
  35. inline size_t taskNumber()
  36. {
  37. return m_taskQ.size();
  38. }
  39. private:
  40. pthread_mutex_t m_mutex;
  41. std::queue<Task<T>> m_taskQ;
  42. };
  • 其中 Task 是任务类,里边有两个成员,分别是两个指针 void(*)(void*) 和 void*;
  • 另外一个类 TaskQueue 是任务队列,提供了添加任务、取出任务、存储任务、获取任务个数、线程同步的功能。

(2)类定义

  1. #include "TaskQueue.h"
  2. template<typename T>
  3. TaskQueue<T>::TaskQueue()
  4. {
  5. pthread_mutex_init(&m_mutex,NULL);
  6. }
  7. template<typename T>
  8. TaskQueue<T>::~TaskQueue()
  9. {
  10. pthread_mutex_destroy(&m_mutex);
  11. }
  12. template<typename T>
  13. void TaskQueue<T>::addTask(Task<T> task)
  14. {
  15. pthread_mutex_lock(&m_mutex);
  16. m_taskQ.push(task);
  17. pthread_mutex_unlock(&m_mutex);
  18. }
  19. template<typename T>
  20. void TaskQueue<T>::addTask(callback f,void* arg)
  21. {
  22. pthread_mutex_lock(&m_mutex);
  23. m_taskQ.push(Task<T>(f,arg));
  24. pthread_mutex_unlock(&m_mutex);
  25. }
  26. template<typename T>
  27. Task<T> TaskQueue<T>::taskTask()
  28. {
  29. Task<T> task;
  30. pthread_mutex_lock(&m_mutex);
  31. if(!m_taskQ.empty())
  32. {
  33. task=m_taskQ.front();
  34. m_taskQ.pop();
  35. }
  36. pthread_mutex_unlock(&m_mutex);
  37. return task;
  38. }

2、线程池

(1) 类声明

  1. #pragma once
  2. #include "TaskQueue.h"
  3. #include "TaskQueue.cpp"
  4. template<typename T>
  5. class ThreadPool
  6. {
  7. private:
  8. //任务队列
  9. TaskQueue<T>* taskQ;
  10. pthread_t managerID; //管理者线程ID
  11. pthread_t* threadIDs; //工作线程ID
  12. int minNum; //最小线程数量
  13. int maxNum; //最大线程数量
  14. int busyNum; //忙的线程个数
  15. int liveNum; //存活的线程个数
  16. int exitNum; //要销毁的线程个数
  17. pthread_mutex_t mutexPool; //锁整个线程池
  18. pthread_cond_t notEmpty; //任务队列是否为空了
  19. bool shutdown; //是不是要销毁线程池,销毁为1,不销毁为0
  20. static const int NUMBER=2;
  21. public:
  22. //创建线程池并初始化
  23. ThreadPool(int min,int max);
  24. //销毁线程池
  25. ~ThreadPool();
  26. //给线程池添加任务
  27. void addTask(Task<T> task);
  28. //获取线程池中工作的线程个数
  29. int getBusyNum();
  30. //获取线程池中活着的线程个数
  31. int getAliveNum();
  32. private:
  33. //工作的线程(消费者线程)任务函数
  34. static void* worker(void* arg);
  35. //管理者线程任务函数
  36. static void* manager(void* arg);
  37. //单个线程退出
  38. void threadExit();
  39. };

(2)类定义

  1. #include "ThreadPool.h"
  2. #include <iostream>
  3. #include <string.h>
  4. #include <string>
  5. #include <unistd.h>
  6. using namespace std;
  7. template<typename T>
  8. ThreadPool<T>::ThreadPool(int min,int max)
  9. {
  10. do
  11. {
  12. //实例化任务队列
  13. taskQ=new TaskQueue<T>;
  14. if(taskQ==nullptr)
  15. {
  16. cout<<"malloc taskQ fail...\n";
  17. break;
  18. }
  19. threadIDs=new pthread_t[max];
  20. if(threadIDs==nullptr)
  21. {
  22. cout<<"malloc threadIDs fail...\n";
  23. break;
  24. }
  25. memset(threadIDs,0,sizeof(pthread_t)*max);
  26. minNum=min;
  27. maxNum=max;
  28. busyNum=0;
  29. liveNum=min; //和最小个数相等
  30. exitNum=0;
  31. if(pthread_mutex_init(&mutexPool,NULL)!=0||
  32. pthread_cond_init(&notEmpty,NULL)!=0)
  33. {
  34. cout<<"mutex or condition init fail...\n";
  35. break;
  36. }
  37. shutdown=false;
  38. //创建管理者线程
  39. //第三个参数在类内部,需要传入已经存在的地址(类实例化才有)
  40. //,因此换成静态成员函数或友元函数;传入this是为了可以
  41. //访问其它成员属性和方法,静态方法只能访问静态的属性
  42. pthread_create(&managerID,NULL,manager,this);
  43. //创建工作线程
  44. for(int i=0;i<min;++i)
  45. {
  46. pthread_create(&threadIDs[i],NULL,worker,this);
  47. }
  48. return;
  49. }while(0);
  50. //创建失败,释放资源
  51. if(threadIDs) delete[] threadIDs;
  52. }
  53. template<typename T>
  54. ThreadPool<T>::~ThreadPool()
  55. {
  56. //关闭线程池
  57. shutdown=true;
  58. //阻塞回收管理者线程池
  59. pthread_join(managerID,NULL);
  60. //唤醒阻塞的工作线程(消费者线程)
  61. for(int i=0;i<liveNum;++i)
  62. {
  63. pthread_cond_signal(&notEmpty);
  64. }
  65. //释放堆内存
  66. if(taskQ)
  67. {
  68. delete taskQ;
  69. }
  70. if(threadIDs)
  71. {
  72. delete []threadIDs;
  73. }
  74. pthread_mutex_destroy(&mutexPool);
  75. pthread_cond_destroy(&notEmpty);
  76. }
  77. template<typename T>
  78. void ThreadPool<T>::addTask(Task<T> task)
  79. {
  80. if(shutdown)//已满
  81. {
  82. return;
  83. }
  84. //添加任务
  85. taskQ->addTask(task);
  86. pthread_cond_signal(&notEmpty);//唤醒工作线程
  87. }
  88. template<typename T>
  89. int ThreadPool<T>::getBusyNum()
  90. {
  91. pthread_mutex_lock(&mutexPool);
  92. int busyNum=this->busyNum;
  93. pthread_mutex_unlock(&mutexPool);
  94. return busyNum;
  95. }
  96. template<typename T>
  97. int ThreadPool<T>::getAliveNum()
  98. {
  99. pthread_mutex_lock(&mutexPool);
  100. int aliveNum=this->liveNum;
  101. pthread_mutex_unlock(&mutexPool);
  102. return aliveNum;
  103. }
  104. template<typename T>
  105. //工作线程
  106. void* ThreadPool<T>::worker(void* arg)
  107. {
  108. //利用传入的this类调用该成员属性和方法
  109. ThreadPool* pool=static_cast<ThreadPool*>(arg);
  110. while(true)
  111. {
  112. pthread_mutex_lock(&pool->mutexPool);
  113. //当前任务队列是否为空
  114. while(pool->taskQ->taskNumber()==0&&!pool->shutdown)
  115. {
  116. //阻塞工作线程
  117. pthread_cond_wait(&pool->notEmpty,&pool->mutexPool);
  118. //判断是否要销毁线程,其实是管理者线程执行这段代码
  119. if(pool->exitNum>0)
  120. {
  121. pool->exitNum--;
  122. if(pool->liveNum>pool->minNum)
  123. {
  124. pool->liveNum--;
  125. pthread_mutex_unlock(&pool->mutexPool);
  126. pool->threadExit();
  127. }
  128. }
  129. }
  130. //判断线程池是否关闭了
  131. if(pool->shutdown)
  132. {
  133. pthread_mutex_unlock(&pool->mutexPool);//释放锁
  134. pool->threadExit();//线程退出
  135. }
  136. //从任务队列中取出一个任务
  137. Task<T> task=pool->taskQ->taskTask();
  138. pool->busyNum++;
  139. //解锁
  140. pthread_mutex_unlock(&pool->mutexPool);
  141. cout<<"thread "<<to_string(pthread_self())<<" start working...\n";
  142. task.function(task.arg);//执行任务函数
  143. delete task.arg;
  144. task.arg=nullptr;
  145. cout<<"thread "<<to_string(pthread_self())<<" end working...\n";
  146. pthread_mutex_lock(&pool->mutexPool);
  147. pool->busyNum--;
  148. pthread_mutex_unlock(&pool->mutexPool);
  149. }
  150. return NULL;
  151. }
  152. template<typename T>
  153. void* ThreadPool<T>::manager(void* arg)
  154. {
  155. ThreadPool* pool=static_cast<ThreadPool*>(arg);
  156. while(!pool->shutdown)
  157. {
  158. //每隔3s检测一次
  159. sleep(3);
  160. //取出线程池中任务的数量、当前线程的数量、忙的线程的数量
  161. pthread_mutex_lock(&pool->mutexPool);;
  162. int queueSize=pool->taskQ->taskNumber();
  163. int liveNum=pool->liveNum;
  164. int busyNum=pool->busyNum;
  165. pthread_mutex_unlock(&pool->mutexPool);
  166. //添加线程
  167. //任务的个数>存活的线程个数&&存活的线程数<最大线程数
  168. if(queueSize>liveNum&&liveNum<pool->maxNum)
  169. {
  170. pthread_mutex_lock(&pool->mutexPool);
  171. int counter=0;//默认最大增加2个工作线程
  172. for(int i=0;i<pool->maxNum&&counter<NUMBER&&pool->liveNum<pool->liveNum<
  173. pool->maxNum;++i)
  174. {
  175. if(pool->threadIDs[i]==0)
  176. {
  177. pthread_create(&pool->threadIDs[i],NULL,worker,pool);
  178. counter++;
  179. pool->liveNum++;
  180. }
  181. }
  182. pthread_mutex_unlock(&pool->mutexPool);
  183. }
  184. //销毁线程
  185. //忙的线程*2<存活的线程数&&存活的线程>最小线程数
  186. if(busyNum*2<liveNum&&liveNum>pool->minNum)
  187. {
  188. pthread_mutex_lock(&pool->mutexPool);
  189. pool->exitNum=NUMBER;
  190. pthread_mutex_unlock(&pool->mutexPool);
  191. //让工作的线程自杀
  192. for(int i=0;i<NUMBER;++i)
  193. {
  194. pthread_cond_signal(&pool->notEmpty);//唤醒阻塞的线程
  195. }
  196. }
  197. }
  198. return NULL;
  199. }
  200. template<typename T>
  201. void ThreadPool<T>::threadExit()
  202. {
  203. pthread_t tid=pthread_self();
  204. for(int i=0;i<maxNum;++i)
  205. {
  206. if(threadIDs[i]==tid)
  207. {
  208. threadIDs[i]=0;
  209. cout<<"threadExit() called, "<<to_string(tid)<<" exiting...\n";
  210. break;
  211. }
  212. }
  213. pthread_exit(NULL);//线程退出
  214. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/621823
推荐阅读
相关标签
  

闽ICP备14008679号