一、为什么要使用线程池?
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。但我们知道线程和创建和销毁的开销很大,所以为了提高效率的一个手段就是尽可能减少创建和销毁对象的次数,然后对这些对象重复使用,而不是频繁的创建和销毁。假设一个服务器完成一个任务所需时间为:T1创建线程时间,T2在线程中执行任务时间,T3线程销毁时间,如果T1+T3远大于T2,则可以通过线程池来提高性能。
线程池是预先创建线程的一种技术,线程池在还没有任务到来之前创建一定数据的线程,放入空闲队列中。这些线程都是处于睡眠状态,即均未启动、不消耗CPU,而只是占用较小的内存空间;当请求到来之后缓冲池给这次请求分配一个空闲线程,把请求传入线程中运行,进行处理;当预先创建的线程都处于运行状态,即预制线程不够,线程池可以自由创建一定数量的新线程,用于处理更多的请求;当系统比较空闲的时候,也可以通过移除一部分一直处于停用状态的线程。
二、线程池的基本组成部分
1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2、工作线程(WorkThread):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3、任务接口(WorkRequest):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4、任务队列(RequestQueue):用于存放没有处理的任务,提供一种缓冲机制;
5、结果队列(ResultQueue):用于存储请求执行后返回的结果
线程池管理器,通过添加请求的方法(putRequest)向任务队列(RequestQueue)添加请求,这些请求事先需要实现请求接口,即传递工作函数、参数、结果处理函数、以及异常处理函数。之后初始化一定数量的工作线程,这些线程通过轮询的方式不断查看任务队列(RequestQueue),只要有请求存在,则会提取出请求,进行执行。然后,线程池管理器调用方法(poll)查看结果队列(resultQueue)是否有值,如果有值,则取出,调用结果处理函数执行。通过以上讲述,不难发现,这个系统的核心资源在于请求队列和结果队列,工作线程通过轮询requestQueue获得人物,主线程通过查看结果队列,获得执行结果。因此,对这个队列的设计,要实现线程同步,以及一定阻塞和超时机制的设计,以防止因为不断轮询而导致的过多cpu开销。在本文中,将会用python语言实现,python的Queue,就是很好的实现了对线程同步机制。
三、线程池的注意事项
虽然线程池是构建多线程应用程序的强大机制,但使用它并不是没有风险的。在使用线程池时需注意线程池大小与性能的关系,注意并发风险、死锁、资源不足和线程泄漏等问题。
(1)线程池大小。多线程应用并非线程越多越好,需要根据系统运行的软硬件环境以及应用本身的特点决定线程池的大小。一般来说,如果代码结构合理的话,线程数目与CPU 数量相适合即可。如果线程运行时可能出现阻塞现象,可相应增加池的大小;如有必要可采用自适应算法来动态调整线程池的大小,以提高CPU 的有效利用率和系统的整体性能。
(2)并发错误。多线程应用要特别注意并发错误,要从逻辑上保证程序的正确性,注意避免死锁现象的发生。
(3)线程泄漏。这是线程池应用中一个严重的问题,当任务执行完毕而线程没能返回池中就会发生线程泄漏现象。