线程池 - 分析与实现(一)
思考
- 线程池的作用?
- 线程池的工作原理?
- 线程池的API有哪些?具体怎么理解?
充电站
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习
线程池
作用
简述来讲,线程池的作用主要两个方面:
1)减少线程创建与销毁;
2) 异步解耦的作用。
稍微长篇的讲,其一就是对于
线程的重用
,线程的创建和销毁的开销是巨大的,而通过线程池的重用大大减少了这些不必要的开销,当然既然少了这么多消费内存的开销,则线程的执行速度起飞了。其二就是对于线程的管理
,线程池可以提供定时、定期、单线程、并发数控制等功能。控制线程池的并发数可以有效的避免大量的线程池争夺CPU资源而造成堵塞。
举个小例子
着重强调异步解耦的作用,以写日志为例,如
loginfo("------\n");
,日志需要落盘,写入磁盘中。我们将日志“落盘”当成一个任务,把这个任务抛给线程池,对于应用程序而言,这样就可以大大提升“落盘”的效率。
“池子”中都些什么?
这里不得不有举个例子来说说了,把线程池看做银行营业厅,银行营业厅里有什么呢?有柜员、办业务的人、公示牌、等等。线程池类似这样的“银行营业厅”
1)柜员–>线程–>执行队列
(很多的柜员);
2)办业务的人–>任务–>任务队列
(很多办业务的人);
3)公式牌–>管理作用–>促使柜员与办业务的人有序进行。
这三项都具备各自的“属性”,后面代码中会介绍。
代码
代码实现
柜员–>线程–>执行队列
// 执行队列->双链表实现 typedef struct NWORKER { pthread_t thread; //线程ID,即工号 int terminate; //flag 终止标志 struct NWORKQUEUE *workqueue; //线程池的对象 struct NWORKER *prev; struct NWORKER *next; } nWorker;
办业务的人–>任务–>任务队列
(
// 任务队列->双链表实现 typedef struct NJOB { void (*job_function)(struct NJOB *job); //回调函数 void *user_data; //参数 struct NJOB *prev; struct NJOB *next; } nJob;
线程池核心
:管理任务队列和执行队列有秩序进行的组件,不要将线程池理解为了连接池。
// 线程池 typedef struct NTREADPOOL { struct NWORKER *workers; //多个柜员 struct NJOB *waiting_jobs; //办业务的人 pthread_mutex_t jobs_mtx; //公示牌->互斥锁 pthread_cond_t jobs_cond; //公示牌->等待条件满足 } nWorkQueue;
用宏定义实现队列的添加和删除
// ADD:在list中添加item(头插法) #define LL_ADD(item, list) do { \ item->prev = NULL; \ item->next = list; \ list = item; \ } while(0)
// REMOVE:在list中删除item #define LL_REMOVE(item, list) do { \ if (item->prev != NULL) item->prev->next = item->next; \ if (item->next != NULL) item->next->prev = item->prev; \ if (list == item) list = item->next; \ item->prev = item->next = NULL; \ } while(0)
总结
本文介绍了线程池用的作用
,以形象的例子说明线程池的重要组成
以及各个部分的代码实现
。下篇将介绍线程池的API
,以及对应的代码实现
。