Linux -- 封装一个线程池
开篇:本章的线程池会用得到前面文章中封装的Mutex,Cond,Thread,Log这些类,如有不解可以看前面文章,此处就不进行讲解了。
线程池是⼀种线程使⽤模式。线程过多会带来调度开销,进⽽影响缓存局部性和整体性能。⽽线程池维护着多个线程,等待着监督管理者分配可并发执⾏的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利⽤,还能防⽌过分调度。可⽤线程数量应该取决于可⽤的并发处理器、处理器内核、内存、⽹络sockets等的数量。
线程池的应⽤场景:
需要⼤量的线程来完成任务,且完成任务的时间⽐较短。 ⽐如WEB服务器完成⽹⻚请求这样的务,使⽤线程池技术是⾮常合适的。因为单个任务⼩,⽽任务数量巨⼤,你可以想象⼀个热⻔⽹站的点击次数。 但对于⻓时间的任务,⽐如⼀个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间⽐线程的创建时间⼤多了。
对性能要求苛刻的应⽤,⽐如要求服务器迅速响应客⼾请求。
接受突发性的⼤量请求,但不⾄于使服务器因此产⽣⼤量线程的应⽤。突发性⼤量客⼾请求,在没有线程池情况下,将产⽣⼤量线程,虽然理论上⼤部分操作系统线程数⽬最⼤值不是问题,短时间 内产⽣⼤量线程可能使内存到达极限,出现错误。
首先我们先明确线程池的框架:成员变量有一个存放线程指针的数组,一个任务队列,线程将会从这里拿任务并执行,条件变量控制当任务队列为空时使线程等待,等待线程数,互斥锁保护任务队列的安全,以及此时线程池的状态。
接着写了构造函数,初始化线程数量,等待的线程数,以及线程池的状态,然后创建Thread数组。这里的Run写的是类函数,所以需要用bind绑定。Run函数内部用先判断任务队列是否为空和线程池状态,如果任务队列为空但是线程池还在运行,那么线程就会进入等待,直到有新的任务到来。如果任务队列为空且线程池不再运行则结束线程,如果有任务线程就会去取任务并执行。
然后完成剩下几个函数的实现:Insert函数用来从外部接受任务,并且每次增加任务以后会唤醒其中一个正在等待的任务。Start函数会将所有的线程都启动,Wait函数用来回收线程,Stop函数将状态设置为暂停,然后唤醒所有正在等待的线程并由他们自己结束。
测试代码:
结果我们可以看到运行的很好,线程也能够完成我们交代的任务,线程之间是并发执行任务的,只不过我们这里限制了派发任务的速度。
完整代码有需要的可以访问我的gitee仓库study_code: 学习路上写的一些代码 (gitee.com),我已经放在ThreadPool文件夹中了,感谢观看。