线程池是
![](/icons/67521yi.gif)
个重要
![](/icons/67521de.gif)
概念
![](/icons/67521dou2.gif)
不过我发现
![](/icons/67521dou.gif)
有关这个话题
![](/icons/67521de.gif)
讨论似乎还缺少了点什么
![](/icons/67521dou2.gif)
作为资料
![](/icons/67521de.gif)
补充
![](/icons/67521dou.gif)
以及今后文章所需要
![](/icons/67521de.gif)
引用
![](/icons/67521dou.gif)
我在这里再完整而又简单地谈
![](/icons/67521yi.gif)
下有关线程池
![](/icons/67521dou.gif)
还有.NET中各种线程池
![](/icons/67521de.gif)
基础
![](/icons/67521dou2.gif)
更详细
![](/icons/67521de.gif)
内容就不多作展开了
![](/icons/67521dou.gif)
有机会我们再详细讨论这方面
![](/icons/67521de.gif)
细节
![](/icons/67521dou2.gif)
这次
![](/icons/67521dou.gif)
还是
![](/icons/67521yi.gif)
个“概述”性质
![](/icons/67521de.gif)
![](/icons/67521dou.gif)
希望可以介绍说明白这方面问题
![](/icons/67521de.gif)
![](/icons/67521yi.gif)
些概念
![](/icons/67521dou2.gif)
线程池
![](/icons/67521de.gif)
作用
其实“线程池”就是用来存放“线程”
![](/icons/67521de.gif)
对象池
![](/icons/67521dou2.gif)
在
![](/icons/67521chengxu.gif)
中
![](/icons/67521dou.gif)
如果某个创建某种对象所需要
![](/icons/67521de.gif)
代价太高
![](/icons/67521dou.gif)
同时这个对象又可以反复使用
![](/icons/67521dou.gif)
那么我们往往就会准备
![](/icons/67521yi.gif)
个容器
![](/icons/67521dou.gif)
用来保存
![](/icons/67521yi.gif)
批这样
![](/icons/67521de.gif)
对象
![](/icons/67521dou2.gif)
于是乎
![](/icons/67521dou.gif)
我们想要用这种对象时
![](/icons/67521dou.gif)
就不需要每次去创建
![](/icons/67521yi.gif)
个
![](/icons/67521dou.gif)
而直接从容器中取出
![](/icons/67521yi.gif)
个现成
![](/icons/67521de.gif)
对象就可以了
![](/icons/67521dou2.gif)
由于节省了创建对象
![](/icons/67521de.gif)
开销
![](/icons/67521dou.gif)
![](/icons/67521chengxu.gif)
性能自然就上升了
![](/icons/67521dou2.gif)
这个容器就是“池”
![](/icons/67521dou2.gif)
很容易理解
![](/icons/67521de.gif)
是
![](/icons/67521dou.gif)
![](/icons/67521yinwei.gif)
有了对象池
![](/icons/67521dou.gif)
因此在用完对象的后必须有
![](/icons/67521yi.gif)
个“归还”
![](/icons/67521de.gif)
动作
![](/icons/67521dou.gif)
这样便可以把对象放回池中
![](/icons/67521dou.gif)
下次需要
![](/icons/67521de.gif)
时候就可以再次拿出来使用了
![](/icons/67521dou2.gif)
例如
![](/icons/67521dou.gif)
我们在使用ADO.NET连接SQL Server时
![](/icons/67521dou.gif)
.NET框架就会自动帮我们维护
![](/icons/67521yi.gif)
个连接池
![](/icons/67521dou.gif)
这就是
![](/icons/67521yinwei.gif)
重新创建
![](/icons/67521yi.gif)
个连接
![](/icons/67521de.gif)
代价相对比较高昂
![](/icons/67521dou.gif)
“复用”就显得比较划算了
![](/icons/67521dou2.gif)
不过有些朋友可能会说
![](/icons/67521dou.gif)
我们明明是每次都创建
![](/icons/67521yi.gif)
个SqlConnection对象
![](/icons/67521dou.gif)
哪里有“复用”啊?这是
![](/icons/67521yinwei.gif)
.NET框架中把“连接池”做透明了
![](/icons/67521dou.gif)
对于
![](/icons/67521chengxu.gif)
员完全隐藏了这个概念
![](/icons/67521dou2.gif)
每次我们虽然创建
![](/icons/67521de.gif)
是新
![](/icons/67521de.gif)
SqlConnection对象
![](/icons/67521dou.gif)
但是这个对象内部占用
![](/icons/67521de.gif)
“数据库连接”还是会复用
![](/icons/67521de.gif)
![](/icons/67521dou2.gif)
为什么总是强
![](/icons/67521diaoyong.gif)
完SqlConnection对象后要及时“关闭”(Dispose或Close)呢?其实这里并没有断开数据库连接
![](/icons/67521dou.gif)
只是把这个连接放回了连接池
![](/icons/67521dou2.gif)
等到下次创建新
![](/icons/67521de.gif)
SqlConnection对象时
![](/icons/67521dou.gif)
这个连接又可以拿出来用了
![](/icons/67521dou2.gif)
既然我们每次都是从池中获取对象
![](/icons/67521dou.gif)
那么这些对象是由谁来创建
![](/icons/67521dou.gif)
又是什么时候创建
![](/icons/67521de.gif)
呢?这个就要根据区别情况由各对象池来自行实现了
![](/icons/67521dou2.gif)
例如
![](/icons/67521dou.gif)
可以在创建对象池
![](/icons/67521de.gif)
时候指定池内对象数量
![](/icons/67521dou.gif)
并且
![](/icons/67521yi.gif)
下子全部创建好
![](/icons/67521dou.gif)
当然您也可以在得到请求时
![](/icons/67521dou.gif)
如果发现池中已经没有剩余对象时创建
![](/icons/67521dou2.gif)
您也可以“事前”先准备
![](/icons/67521yi.gif)
部分
![](/icons/67521dou.gif)
“事中”根据需要再继续补充
![](/icons/67521dou2.gif)
还可以做得“智能”
![](/icons/67521yi.gif)
些
![](/icons/67521dou.gif)
例如
![](/icons/67521dou.gif)
根据实际情况添加或删除
![](/icons/67521yi.gif)
些对象
![](/icons/67521dou.gif)
甚至对需求“走势”进行“预测”
![](/icons/67521dou.gif)
在空闲时便创建更多
![](/icons/67521de.gif)
对象以备“不时的需”
![](/icons/67521dou2.gif)
各中变化难以言尽
![](/icons/67521dou2.gif)
当然
![](/icons/67521dou.gif)
它们
![](/icons/67521de.gif)
原理和目
![](/icons/67521de.gif)
是类似
![](/icons/67521de.gif)
![](/icons/67521dou2.gif)
相信上面这段文字也已经讲清了“线程池”
![](/icons/67521de.gif)
作用:
![](/icons/67521yinwei.gif)
创建
![](/icons/67521yi.gif)
个线程
![](/icons/67521de.gif)
代价较高
![](/icons/67521dou.gif)
因此我们使用线程池设法复用线程
![](/icons/67521dou2.gif)
就是这么简单
![](/icons/67521dou2.gif)
CLR线程池
![](/icons/67521de.gif)
作用
在.NET中
![](/icons/67521dou.gif)
CLR线程和操作系统线程对应
![](/icons/67521dou.gif)
您可以简单地认为.NET中
![](/icons/67521de.gif)
Thread对象便封装了
![](/icons/67521yi.gif)
个操作系统线程
![](/icons/67521dou.gif)
并附带
![](/icons/67521yi.gif)
些托管环境下所需要
![](/icons/67521de.gif)
数据(如GC Handle)1
![](/icons/67521dou2.gif)
而CLR线程池便是存放这些CLR线程
![](/icons/67521de.gif)
对象池
![](/icons/67521dou2.gif)
我们在编写
![](/icons/67521chengxu.gif)
![](/icons/67521de.gif)
时候
![](/icons/67521dou.gif)
可以使用ThreadPool类
![](/icons/67521de.gif)
两个静态思路方法:QueueUserWorkItem和UnsafeUserQueueWorkItem向CLR线程池中添加任务(
![](/icons/67521yi.gif)
个WorkCallback委托对象)
![](/icons/67521dou.gif)
这两个思路方法
![](/icons/67521de.gif)
区别
![](/icons/67521dou.gif)
在于前者会收集
![](/icons/67521diaoyong.gif)
方
![](/icons/67521de.gif)
ExecutionContext
![](/icons/67521dou.gif)
也就是保留了
![](/icons/67521de.gif)
当前线程
![](/icons/67521de.gif)
执行信息(如认证或语言文化等)
![](/icons/67521dou.gif)
使任务最终会在“创建”时刻
![](/icons/67521de.gif)
环境中执行2——后者就不会
![](/icons/67521dou2.gif)
因此
![](/icons/67521dou.gif)
如果比较两个思路方法
![](/icons/67521de.gif)
绝对性能
![](/icons/67521dou.gif)
Unsafe思路方法会略胜
![](/icons/67521yi.gif)
筹
![](/icons/67521dou2.gif)
但是平时还是建议使用QueueUserWorkItem思路方法
![](/icons/67521dou.gif)
![](/icons/67521yinwei.gif)
保留执行上下文会避免很多麻烦事情
![](/icons/67521dou.gif)
且这点性能损耗其实算不上什么
![](/icons/67521dou2.gif)
CLR线程池在.NET框架中
![](/icons/67521de.gif)
作用很大
![](/icons/67521dou.gif)
除了让
![](/icons/67521chengxu.gif)
员使用的外
![](/icons/67521dou.gif)
其他
![](/icons/67521yi.gif)
些功能也会依赖CLR线程池
![](/icons/67521dou2.gif)
如ThreadPool.RegisterWaitForSingleObject思路方法
![](/icons/67521dou.gif)
或是
![](/icons/67521System.gif)
.Threading.Timer组件——还有更重要可能也是更隐藏
![](/icons/67521de.gif)
:ASP.NET在得到
![](/icons/67521yi.gif)
个请求后
![](/icons/67521dou.gif)
也会将这个请求处理
![](/icons/67521de.gif)
任务交由CLR线程池去执行——请注意
![](/icons/67521dou.gif)
它们最多只是添加任务而已
![](/icons/67521dou.gif)
并不表示任务会立即执行
![](/icons/67521dou2.gif)
所有添加到CLR线程池
![](/icons/67521de.gif)
任务都会在合适
![](/icons/67521de.gif)
时候得以执行——可能马上
![](/icons/67521dou.gif)
也可能要稍等片刻
![](/icons/67521dou.gif)
甚至更久
![](/icons/67521dou2.gif)
向CLR线程池添加任务时
![](/icons/67521dou.gif)
任务会被临时放到
![](/icons/67521yi.gif)
个队列中
![](/icons/67521dou.gif)
并在合适
![](/icons/67521de.gif)
时候执行
![](/icons/67521dou2.gif)
那么如何样才算是“合适
![](/icons/67521de.gif)
时候”?简单
![](/icons/67521de.gif)
概括说来
![](/icons/67521dou.gif)
便是线程池内有空闲
![](/icons/67521de.gif)
线程
![](/icons/67521dou.gif)
或线程池所管理
![](/icons/67521de.gif)
线程数量还没有达到上限
![](/icons/67521de.gif)
时候
![](/icons/67521dou2.gif)
如果有空闲
![](/icons/67521de.gif)
线程
![](/icons/67521dou.gif)
线程池就会立即让它领取
![](/icons/67521yi.gif)
个任务执行
![](/icons/67521dou2.gif)
如果是第 2种情况
![](/icons/67521dou.gif)
线程池便会创建新
![](/icons/67521de.gif)
Thread对象
![](/icons/67521dou2.gif)
由于让操作系统管理太多线程反而会造成性能下降
![](/icons/67521dou.gif)
因此CLR线程池会有
![](/icons/67521yi.gif)
个上限
![](/icons/67521dou2.gif)
区别
![](/icons/67521de.gif)
托管环境会设置区别
![](/icons/67521de.gif)
上限
![](/icons/67521dou2.gif)
如在.NET 2.0 SP1的后
![](/icons/67521dou.gif)
普通
![](/icons/67521de.gif)
Windows应用
![](/icons/67521chengxu.gif)
(如控制台或WinForm/WPF)
![](/icons/67521dou.gif)
会将其设置为“处理器数 * 250”
![](/icons/67521dou2.gif)
也就是说
![](/icons/67521dou.gif)
如果您
![](/icons/67521de.gif)
机器为2个2核CPU
![](/icons/67521dou.gif)
那么CLR线程池
![](/icons/67521de.gif)
容量默认上限便是1000
![](/icons/67521dou.gif)
也就是说
![](/icons/67521dou.gif)
它最多可以管理1000个线程同时运行——很多情况下这已经是
![](/icons/67521yi.gif)
个很可怕
![](/icons/67521de.gif)
数字了
![](/icons/67521dou.gif)
如果您觉得这还不够
![](/icons/67521dou.gif)
那么就应该考虑
![](/icons/67521yi.gif)
下您
![](/icons/67521de.gif)
实现方式是否可以改进了
![](/icons/67521dou2.gif)
对于ASP.NET应用
![](/icons/67521chengxu.gif)
来说
![](/icons/67521dou.gif)
CLR线程池容量代表了应用
![](/icons/67521chengxu.gif)
最多可以同时执行
![](/icons/67521de.gif)
请求数量
![](/icons/67521dou2.gif)
对于托管在IIS上
![](/icons/67521de.gif)
ASP.NET执行环境来说
![](/icons/67521dou.gif)
这个值由全局配置决定
![](/icons/67521dou2.gif)
这个配置在machine.config文件中system.web/processModel节点中
![](/icons/67521dou.gif)
为maxWorkerThreads属性
![](/icons/67521dou.gif)
它决定了为单个处理器分配
![](/icons/67521de.gif)
线程数
![](/icons/67521dou2.gif)
如果这个值为40
![](/icons/67521dou.gif)
且机器上拥有4个处理器(2 * 2CPU)
![](/icons/67521dou.gif)
那么这台机器目前
![](/icons/67521de.gif)
配置表示在同
![](/icons/67521yi.gif)
时刻
![](/icons/67521dou.gif)
ASP.NET可以同时处理160个请求
![](/icons/67521dou2.gif)
某些参考资料建议您将其修改为每处理器80-100个线程
![](/icons/67521dou.gif)
这时您只要修改相应
![](/icons/67521de.gif)
属性值就可以了
![](/icons/67521dou2.gif)
既然有最大值
![](/icons/67521dou.gif)
也就相应有了最小值
![](/icons/67521dou.gif)
它代表了CLR线程池“总是会保留”
![](/icons/67521de.gif)
最少线程数量
![](/icons/67521dou2.gif)
由于线程会占用资源
![](/icons/67521dou.gif)
如在默认情况下
![](/icons/67521dou.gif)
每个线程将获得1MB大小
![](/icons/67521de.gif)
栈空间3
![](/icons/67521dou2.gif)
所以如果在系统中保留太多空闲线程对资源也是
![](/icons/67521yi.gif)
种浪费
![](/icons/67521dou2.gif)
因此
![](/icons/67521dou.gif)
CLR线程池在使用大量线程处理完大量任务的后
![](/icons/67521dou.gif)
也会逐步地释放线程
![](/icons/67521dou.gif)
直至到达最小值
![](/icons/67521dou2.gif)
CLR线程池
![](/icons/67521de.gif)
最小线程数量确保了在任务数量较少
![](/icons/67521de.gif)
情况下
![](/icons/67521dou.gif)
新来
![](/icons/67521de.gif)
任务可以立即执行
![](/icons/67521dou.gif)
从而省去了创建新线程
![](/icons/67521de.gif)
时间
![](/icons/67521dou2.gif)
在普通应用
![](/icons/67521chengxu.gif)
中这个值为“处理器数 * 1”
![](/icons/67521dou.gif)
而在ASP.NET应用
![](/icons/67521chengxu.gif)
中这个值配置在machine.config文件中system.web/processModel节点
![](/icons/67521de.gif)
minWorkerThreads属性中4
![](/icons/67521dou2.gif)
在某些时候可能会遇到这样
![](/icons/67521de.gif)
情况:在
![](/icons/67521yi.gif)
个瞬间忽然来大量任务
![](/icons/67521dou.gif)
每个任务
![](/icons/67521de.gif)
执行时间说长不长说短不短
![](/icons/67521dou.gif)
不过足以导致线程池快速分配数百个线程
![](/icons/67521dou2.gif)
如果这个峰值的后就
![](/icons/67521yi.gif)
片平静
![](/icons/67521dou.gif)
那么势必造成大量空闲
![](/icons/67521de.gif)
线程
![](/icons/67521dou.gif)
这种开销对性能
![](/icons/67521de.gif)
损耗也非常明显
![](/icons/67521dou2.gif)
因此
![](/icons/67521dou.gif)
CLR线程池限制了线程
![](/icons/67521de.gif)
创建速度不超过每秒2个
![](/icons/67521dou2.gif)
这样
![](/icons/67521dou.gif)
即使在某个瞬时获得了大量
![](/icons/67521de.gif)
任务
![](/icons/67521dou.gif)
CLR线程池也可以使用相对较少
![](/icons/67521de.gif)
线程来完成所有工作5
![](/icons/67521dou2.gif)
但是
![](/icons/67521dou.gif)
还有
![](/icons/67521yi.gif)
种情况也值得考虑
![](/icons/67521dou2.gif)
例如
![](/icons/67521dou.gif)
对于
![](/icons/67521yi.gif)
个比较繁忙
![](/icons/67521de.gif)
Web应用
![](/icons/67521chengxu.gif)
来说
![](/icons/67521dou.gif)
![](/icons/67521yi.gif)
打开便会涌入大量
![](/icons/67521de.gif)
连接
![](/icons/67521dou2.gif)
由于线程
![](/icons/67521de.gif)
创建速度有限
![](/icons/67521dou.gif)
因此可以执行
![](/icons/67521de.gif)
请求数量也只能慢慢增加
![](/icons/67521dou2.gif)
对于这种您预料到会产生大量线程
![](/icons/67521dou.gif)
而且忙碌状况会持续
![](/icons/67521yi.gif)
段时间
![](/icons/67521de.gif)
情况
![](/icons/67521dou.gif)
限制线程
![](/icons/67521de.gif)
创建速度反而会带来损伤效率
![](/icons/67521dou2.gif)
这时
![](/icons/67521dou.gif)
您就可以手动设置CLR线程池
![](/icons/67521de.gif)
最小线程数量
![](/icons/67521dou2.gif)
如果此时CLR线程池中拥有
![](/icons/67521de.gif)
线程数量较少
![](/icons/67521dou.gif)
那么系统就会立即创建
![](/icons/67521yi.gif)
定数量
![](/icons/67521de.gif)
线程来达到这个最小值
![](/icons/67521dou2.gif)
设置和获取CLR线程池最小线程数量
![](/icons/67521de.gif)
接口为:
public
ThreadPool
{
public
void GetMinThreads(out
workerThreads, out
completionPortThreads);
public
bool SetMinThreads(
workerThreads,
completionPortThreads);
}
这两个接口
![](/icons/67521de.gif)
作用和使用方式应该足够明显了(不理解
![](/icons/67521de.gif)
话可以查阅MSDN)
![](/icons/67521dou.gif)
其中workerThreads参数便是CLR线程池
![](/icons/67521de.gif)
最小线程数
![](/icons/67521dou.gif)
而completionPortThreads涉及到我们下次要讨论IO线程池
![](/icons/67521dou.gif)
在此就不多作展开了
![](/icons/67521dou2.gif)
除了设置和读取CLR最小线程数
![](/icons/67521de.gif)
思路方法的外
![](/icons/67521dou.gif)
ThreadPool还包含这些接口:
public
ThreadPool
{
public
void GetMaxThreads(out
workerThreads, out
completionPortThreads);
public
bool SetMaxThreads(
workerThreads,
completionPortThreads);
public
void GetAvailableThreads(out
workerThreads, out
completionPortThreads);
}
值得注意
![](/icons/67521de.gif)
是
![](/icons/67521dou.gif)
无论是设置还是获取到
![](/icons/67521de.gif)
这些数值
![](/icons/67521dou.gif)
都和处理器数量没有任何关系了
![](/icons/67521dou2.gif)
也就是说
![](/icons/67521dou.gif)
在
![](/icons/67521yi.gif)
台2 * 2CPU
![](/icons/67521de.gif)
机器上运行
![](/icons/67521yi.gif)
个普通
![](/icons/67521de.gif)
.NET应用
![](/icons/67521chengxu.gif)
时:
![](/icons/67521diaoyong.gif)
GetMaxThreads思路方法将获得1000
![](/icons/67521dou.gif)
表示CLR线程池最大容量为1000(250 * 4)
![](/icons/67521dou.gif)
而不是250
![](/icons/67521diaoyong.gif)
SetMinThreads并传入100
![](/icons/67521dou.gif)
表示CLR线程池所拥有
![](/icons/67521de.gif)
最小线程数量为100
![](/icons/67521dou.gif)
而不是400(100 * 4)
![](/icons/67521dou2.gif)
对于CLR线程池
![](/icons/67521de.gif)
作用
![](/icons/67521de.gif)
简单描述就暂时先到这里了
![](/icons/67521dou2.gif)
如果您还有什么疑问请提出
![](/icons/67521dou.gif)
我会加以补充
![](/icons/67521dou2.gif)
注1:严格说来
![](/icons/67521dou.gif)
Thread对象和系统线程对应关系还有些细节上
![](/icons/67521de.gif)
考虑
![](/icons/67521dou2.gif)
例如
![](/icons/67521dou.gif)
Thread对象只有当真正Start了的后
![](/icons/67521dou.gif)
CLR才会创建
![](/icons/67521yi.gif)
个操作系统线程和它绑定
![](/icons/67521dou2.gif)
注2:ExecutionContext是个很重要且很有用
![](/icons/67521de.gif)
对象
![](/icons/67521dou.gif)
例如
![](/icons/67521dou.gif)
WinForms或WPF
![](/icons/67521de.gif)
异步任务中操作界面元素抛出异常该如何办呢?
注3:使用Windows API或Thread类创建线程时可以指定它
![](/icons/67521de.gif)
栈空间大小
![](/icons/67521dou.gif)
但是CLR线程池中
![](/icons/67521de.gif)
线程只能使用默认值——不过这个默认值也和托管环境有关
![](/icons/67521dou.gif)
如普通应用
![](/icons/67521chengxu.gif)
默认为1MB
![](/icons/67521dou.gif)
而ASP.NET为250KB
![](/icons/67521dou.gif)
这意味着ASP.NET应用
![](/icons/67521chengxu.gif)
相对更容易产生Stack Overflow异常
![](/icons/67521dou2.gif)
注4:可惜
![](/icons/67521de.gif)
是
![](/icons/67521dou.gif)
对于processModel节点
![](/icons/67521de.gif)
数据
![](/icons/67521dou.gif)
ASP.NET只会读取machine.config中
![](/icons/67521de.gif)
全局配置信息
![](/icons/67521dou.gif)
这意味着我们不能使用web.config为区别应用
![](/icons/67521chengxu.gif)
配置区别
![](/icons/67521de.gif)
参数
![](/icons/67521dou2.gif)
如果我们要实现应用
![](/icons/67521chengxu.gif)
级别
![](/icons/67521de.gif)
配置
![](/icons/67521dou.gif)
那么必须使用ThreadPool类中提供
![](/icons/67521de.gif)
API进行设置
![](/icons/67521dou.gif)
这点稍后便会提到
![](/icons/67521dou2.gif)
注5:对于这点
![](/icons/67521dou.gif)
您不妨来做
![](/icons/67521yi.gif)
个算术题:线程池内
![](/icons/67521yi.gif)
下子涌入了500个任务
![](/icons/67521dou.gif)
每个任务阻塞或暂停5秒
![](/icons/67521dou.gif)
每个线程占用1MB内存
![](/icons/67521dou.gif)
假设线程池目前为空
![](/icons/67521dou.gif)
且有着足够
![](/icons/67521de.gif)
容量
![](/icons/67521dou.gif)
此外线程创建速度也足够快
![](/icons/67521dou.gif)
那么在限制及不限制线程创建速度
![](/icons/67521de.gif)
情况下
![](/icons/67521dou.gif)
完成这些任务需要多少时间和内存空间?