.NET中的各种池

在.NET中,常用到的池有四个:字符串拘留池、线程池 、应用程序池、数据库连接池

字符串拘留池

在.NET中字符串是不可变对象,修改字符串变量的值会产生新的对象。为降低性能消耗及减小程序集大小,.NET提供了string interning的功能,直译过来就是字符串拘留。所谓的字符串拘留池(intern pool)其实是一张哈希表,键是字符串字面量,值是托管堆上字符串对象的引用。但若该表过大,则会对性能造成负面影响。在加载程序集时,不同版本的CLR对于是否留用程序集元数据中的字符串字面量(在编译时值已确定)不尽相同。但显式调用string.Intern方法则会将字符串字面量放入池中。

我们在给string类型变量分配字面量值时,CLR会先到字符串池中看下有没有完全相同的字符串(区分大小写),若有则返回对应的引用,若无,则创建新对象并添加到字符串池中返回引用。但若在运行时(如,使用new关键字)来给字符串变量分配值则不会使用字符串池。

C#提供了和字符串池相关的两个方法:

//若str不在字符串池中就创建新字符串对象放到池里并返回引用
public staticc String Intern(String str);
//若str不在字符串池中不会创建新字符串对象并返回null
public staticc String IsInterned(String str);

示例代码如下:

var str = "abc";
var str01 = "abc";
//运行时常量
var str02 = new string(new char[] { 'a', 'b', 'c' });
//编译时常量(可通过反编译器查看编译后的代码)
string str03 = "a" + "bc";

Console.WriteLine($"str01==str is {ReferenceEquals(str01, str)}");
Console.WriteLine($"str02==str is {ReferenceEquals(str02, str)}");
Console.WriteLine($"str03==str is {ReferenceEquals(str03, str)}");

var str04 = String.IsInterned(new string(new char[] { 'a', 'b' }));
Console.WriteLine($"str04 == null is {str04 == null}");
var str05 = String.IsInterned("abdgj");
Console.WriteLine($"str05={str05}");

var str06 = String.Intern(new string(new char[] { 'a', 'b', 'd', 'e' }));
Console.WriteLine($"str06={str06}");

得到如下结果:


线程池

一个进程中只有一个线程池(MSDN)。另一种说法是,一个CLR中一个线程池(《CLR via C#》),我认同这种说法。一个进程可以加载多个不同版本的CLR,但同一版本的CLR只能有一个。总之,线程不属于应用程序域(AppDomain)。

若线程池中的线程存在未处理的异常,则会导致当前进程被终止,但有三个例外:

在.NET1.0和1.1版本中, CLR会处理掉线程池中未处理的异常。但这样做会破坏应用程序中的状态甚至导致程序挂起,这些不利于调试。

在.NET中,许多场景可以使用线程池。如,异步I/O,回调,注册wait操作,使用委托的异步方法调用及System.Net 中的socket连接。

但在如下场景中应避免使用线程池中的线程:

  • 需要使用前台线程时
  • 线程需要特定优先级时
  • 需要执行比较耗时的操作时。因为线程池中的线程数有上限,因此长时间的阻塞可能会影响其它任务的处理
  • 当需要放置线程在单线程单元(single-threaded apartment)时。线程池中的线程均在多线程单元(multithreaded apartment)中
  • 需要给线程一个稳定的标识或者线程用于特定任务时

线程池中的线程分为两种:工作线程(Worker)I/O线程(I/O Completion Port)这两种线程只是用处不同,并无本质区别

线程池中的最小线程数默认为处理器的逻辑核心数。即,在4核计算机上,线程池中工作线程和I/O线程默认的最小数均为4。理论上,线程池中的最大线程数只受可用内存大小限制,但是线程池会限制进程内可用线程的数量。

ThreadPool.GetMinThreads(out var minWorkerThreadCount, out var minIoThreadCount);
Console.WriteLine($"minWorkerThreadCount={minWorkerThreadCount},minIoThreadCount={minIoThreadCount}");
ThreadPool.GetMaxThreads(out var maxWorkerThreadCount, out var maxIoThreadCount);
Console.WriteLine($"maxWorkerThreadCount={maxWorkerThreadCount},maxIoThreadCount={maxIoThreadCount}");

运算结果如下:


当应用使用线程池中的线程进行工作时,若线程池中没有线程,则会创建新的线程以满足需要,当线程池中的线程数达到设定的最小线程数且无空闲线程时,则会先等待一段时间(最多500ms),500ms过后依然没有空闲线程可供使用则会创建新线程进行工作,但线程池中的线程数不会超过设定的最大线程数。

当线程池中的线程处于空闲状态一段时间后(不同CLR,这个时间不同),会被销毁。

当应用负载较低时,线程池中的线程数也有可能小于设定的最小线程数。

machine.config中线程池配置如下(.NET 配置文件体系参见:ASP.NET Configuration File Hierarchy and Inheritance):

<system.web>
     <processModel autoConfig="true"/>
</system.web>

配置线程池大小:

//这种配置方式和处理CPU逻辑核心数无关
ThreadPool.SetMaxThreads(1000, 800);
ThreadPool.SetMinThreads(20, 20);

ASP.NET也可通过配置文件进行配置,这种方式是针对每个CPU逻辑核心进行配置:

<configuration>
 <system.web>
    <processModel minWorkerThreads="20" minIoThreads="20" />
 </system.web>
</configuration>

这样做,在应用启动后会报错:在 machine.config 文件之外使用注册为 allowDefinition='MachineOnly' 的节是错误的。需要修改machine.config文件。

线程池配置得当对于应用性能提升是有不少帮助的

应用程序池

IIS5中,一台服务器只有一个工作进程,不同应用使用AppDomain进行区分,当工作进程出现问题,所有应用都会受到影响。从IIS6开始引入了应用程序池的概念,应用程序池通过进程来隔离不同的应用程序以防止不同应用之间相互影响。在部署ASP.NET应用时,应用程序池通常有两种托管管道模式可供选择:集成模式和经典模式

默认情况下,一个应用程序池有一个工作进程,可以根据实际情况设置多个工作进程,但要考虑资源消耗及本地缓存同步问题。

IIS6和IIS5中的工作进程隔离均是在服务器级别。在同一台服务器上无法使用不同的工作进程隔离模式。从IIS7开始,工作进程隔离模式是基于应用程序池的,这样就可以在同一台服务器上使用不同的隔离模式。

在应用程序池——高级设置中可以对应用程序池做相关设置,如队列长度,工作进程回收机制等。

数据库连接池

和数据库服务器建立连接的过程是比较耗时的,对此,ADO.NET中使用了连接池来进行优化。在.NET中不同的Data Provider对于连接池的处理方式不尽相同。默认情况下,ADO.NET 启用连接池优化,可以通过连接字符串来配置是否启用连接池。

连接池可以减少和数据库建立连接的次数,连接池中维护着一组活跃的数据库连接。在我们调用IDbConnectionOpen方法时,CLR会去连接池中寻找是否有可用的连接,若有则返回该连接而无需与数据库建立新的连接。当我们调用IDbConnectionClose方法时,连接会被连接池回收但不断开与数据库的连接,以备下次使用。连接池中的连接空闲一段时间(约4~8分钟)后或者连接池检测到连接已与服务器断开(需要与服务器通讯才能检测连接是否已断开),那么该连接将会被销毁。

在第一次打开连接时,ADO.NET会根据连接配置来建立连接池。ADO.NET为每个连接配置创建一个连接池,所以若程序中用到多个不同的连接配置(如,不同的连接字符串),则会有多个连接池。

若连接池中发生了超时或者其它登录错误,则会抛出异常,那么在接下来的5s内尝试该连接都将失败,这5s钟成为阻塞期。若阻塞期结束后的连接再次失败,则会进入一个新的阻塞期,新的阻塞期时长是上个阻塞期时长的2倍,但最多不超过1分钟。

如果连接字符串中没有设置MinPoolSize的值,或者将该值设为0,那么当池中没有活动连接时,连接池也会被销毁。但若将MinPoolSize的值设为大于0,那么只有在卸载AppDomain时,连接池才会被销毁。当连接池中发生了较为严重的错误,连接池也会自我清理。

连接池中最大连接数默认为100,当连接池中连接数已达到上限,且均被占用,那么新的请求会进入队列等到,等待时间超过15s(默认)则会抛出异常。

数据库连接推荐使用如下写法,这样using语句结束后,连接对象会回到连接池中以便下次请求使用。

using (IDbConnection conn = new SqlConnection())
{

}

除了客户端维持的连接池外,数据库服务本身还有连接数的限制

SQL Server最大连接数限制

我将数据库最大并发连接数设为1,ADO.NET连接池使用默认配置,循环打开3000个连接并未发生异常:

for (var i = 0; i < 3000; i++)
{
    var conn = new SqlConnection(connStr);
    conn.Open();
}

但若将连接池最大连接数设置成一个比较小的数,如3,再次执行上述代码则会发生异常:


按这种情况看,连接池中的连接公用了同一个数据库连接。在连接池连接数耗尽时,则因为等待超时而抛出异常。

但我并没有悟出数据库最大并发连接数的奥妙,哪位童鞋知道,麻烦告知,在此表示感谢!

结语

以上,是本人学习的一点儿心得,错误之处望大家多多指教。

推荐阅读

Thread Pool
Exceptions in Managed Threads.
StackExchange.Redis Timeout
记5.28大促压测的性能优化—线程池相关问题(线程池配置不当导致)
工作者线程(worker thread)和I/O线程


Introduction to IIS Architectures
ASP.NET Integration with IIS 7
ASP.NET Configuration File Hierarchy and Inheritance
IIS与ASP.NET中的线程池
iis最大连接数和队列长度
System.Threading.Tasks.Task引起的IIS应用程序池崩溃
HTTP.SYS 详解
IIS执行原理
IIS ASP.NET的进程模式浅析


SQL Server Connection Pooling (ADO.NET)
Connection Pooling
SqlServer数据库连接数与客户端连接池关系测试(一)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容