前言

线程池管理机制随着CLR的版本不断变化,最好将线程池看成一个黑盒。它不是针对某一种程序设计的,也不适合用某个程序去衡量它的性能。目前来看工作情况还是不错的,随着CLR版本的迭代也会不断进行优化,所以一般不需要对线程池做过多的调整。

线程池如何管理工作者线程

【CLR】C#线程池如何管理线程_clr via c#

工作原理大致如图所示:


  1. 其它线程将需要做的任务(task)放入到线程池的全局队列中,如​​ThreadPool.QueneUserWorkItem​​方法。
  2. 工作者线程不断的消费本地队列,消费顺序是后进先出(类似于栈),每次从列头拿出一个task进行处理。注意:本地队列列头的task只能有与之关联的工作者线程访问,而列尾的task可以被其它工作线程访问。所以如果一个工作者线程消费完自己队列之后,是可以从其它工作者线程队列的列尾拿task进行消费的(通过线程同步锁)。
  3. 如果所有的本地队列都空了,则工作者线程开始以先进先出的方式从全局队列拿去task并放入到自己的本地队列中。
  4. 如果全局队列长时间空,则工作者线程会sleep,过了一段时间之后发现还是为空,则会销毁自身。

线程池设置

正如开发者都不会限制自己程序使用多少多少内存、多少多少带宽,一般情况下线程池也不需要进行特别设置,设置不好会弄巧成拙。

如果非要设置的话,​​ThreadPool​​​提供了几个静态方法,​​GetMaxThreads​​​,​​SetMaxThreads​​​,​​GetMinThreads​​​,​​SetMinThreads​​。用来获取或设置最大最小线程数。

实践证明,永远都不要设置线程池的最大线程数,因为有可能会发生死锁或者饥饿。举个栗子:假如有1000个task正在被处理,但是因为某个事件被阻塞了,需要等待第1001个task发出信号才能继续执行。如果你设置了线程最大数是1000,那这1000个线程就全部被锁住了。

随着CLR版本的更新,线程池默认最大线程数也会不断调整,目前默认值是1000。32位进程最大可以有2GB的内存空间,除去本地堆和托管堆的占用,差不多还有1.5G,大约最多有1360个线程。所以1000个还觉得不够用,那应该是你的程序出现了问题。

线程池的最小线程数等于你进程允许使用的CPU核数。

线程池会根据task的完成速度(具体是怎么判断的,还木有找到),动态的增加和减少总的线程数。