前言
线程池管理机制随着CLR的版本不断变化,最好将线程池看成一个黑盒。它不是针对某一种程序设计的,也不适合用某个程序去衡量它的性能。目前来看工作情况还是不错的,随着CLR版本的迭代也会不断进行优化,所以一般不需要对线程池做过多的调整。
线程池如何管理工作者线程
工作原理大致如图所示:
- 其它线程将需要做的任务(task)放入到线程池的全局队列中,如
ThreadPool.QueneUserWorkItem
方法。 - 工作者线程不断的消费本地队列,消费顺序是后进先出(类似于栈),每次从列头拿出一个task进行处理。注意:本地队列列头的task只能有与之关联的工作者线程访问,而列尾的task可以被其它工作线程访问。所以如果一个工作者线程消费完自己队列之后,是可以从其它工作者线程队列的列尾拿task进行消费的(通过线程同步锁)。
- 如果所有的本地队列都空了,则工作者线程开始以先进先出的方式从全局队列拿去task并放入到自己的本地队列中。
- 如果全局队列长时间空,则工作者线程会sleep,过了一段时间之后发现还是为空,则会销毁自身。
线程池设置
正如开发者都不会限制自己程序使用多少多少内存、多少多少带宽,一般情况下线程池也不需要进行特别设置,设置不好会弄巧成拙。
如果非要设置的话,ThreadPool
提供了几个静态方法,GetMaxThreads
,SetMaxThreads
,GetMinThreads
,SetMinThreads
。用来获取或设置最大最小线程数。
实践证明,永远都不要设置线程池的最大线程数,因为有可能会发生死锁或者饥饿。举个栗子:假如有1000个task正在被处理,但是因为某个事件被阻塞了,需要等待第1001个task发出信号才能继续执行。如果你设置了线程最大数是1000,那这1000个线程就全部被锁住了。
随着CLR版本的更新,线程池默认最大线程数也会不断调整,目前默认值是1000。32位进程最大可以有2GB的内存空间,除去本地堆和托管堆的占用,差不多还有1.5G,大约最多有1360个线程。所以1000个还觉得不够用,那应该是你的程序出现了问题。
线程池的最小线程数等于你进程允许使用的CPU核数。
线程池会根据task的完成速度(具体是怎么判断的,还木有找到),动态的增加和减少总的线程数。