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)。

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

ThreadAbortException ,在调用 Abort 方法终止线程时会抛出该异常

AppDomainUnloadedException ,在卸载AppDomain时会抛出该异常

CLR或宿主进程终止一个线程时

在.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>

</system.web>
配置线程池大小:

//这种配置方式和处理CPU逻辑核心数无关
ThreadPool.SetMaxThreads(1000, 800);
ThreadPool.SetMinThreads(20, 20);
ASP.NET也可通过配置文件进行配置,这种方式是针对每个CPU逻辑核心进行配置:

<system.web>

</system.web>

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

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

应用程序池

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

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

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

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

数据库连接池

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

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

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

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

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

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

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

using (IDbConnection conn = new SqlConnection())
{

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

推荐阅读
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

版权声明

NET中的池的更多相关文章

  1. Java中java.util.concurrent包下的4中线程池代码示例

    先来看下ThreadPool的类结构 其中红色框住的是常用的接口和类(图片来自:https://blog.csdn.net/panweiwei1994/article/details/78617117 ...

  2. Python多进程库multiprocessing中进程池Pool类的使用[转]

    from:http://blog.csdn.net/jinping_shi/article/details/52433867 Python多进程库multiprocessing中进程池Pool类的使用 ...

  3. Java中线程池,你真的会用吗?

    在<深入源码分析Java线程池的实现原理>这篇文章中,我们介绍过了Java中线程池的常见用法以及基本原理. 在文中有这样一段描述: 可以通过Executors静态工厂构建线程池,但一般不建 ...

  4. Java并发编程中线程池源码分析及使用

    当Java处理高并发的时候,线程数量特别的多的时候,而且每个线程都是执行很短的时间就结束了,频繁创建线程和销毁线程需要占用很多系统的资源和时间,会降低系统的工作效率. 参考http://www.cnb ...

  5. 沉淀再出发:java中线程池解析

    沉淀再出发:java中线程池解析 一.前言 在多线程执行的环境之中,如果线程执行的时间短但是启动的线程又非常多,线程运转的时间基本上浪费在了创建和销毁上面,因此有没有一种方式能够让一个线程执行完自己的 ...

  6. Java中线程池,你真的会用吗?ExecutorService ThreadPoolExcutor

    原文:https://www.hollischuang.com/archives/2888 在<深入源码分析Java线程池的实现原理>这篇文章中,我们介绍过了Java中线程池的常见用法以及 ...

  7. JAVA和C#中数据库连接池原理与应用

    JAVA和C#中数据库连接池原理 在现在的互联网发展中,高并发成为了主流,而最关键的部分就是对数据库操作和访问,在现在的互联网发展中,ORM框架曾出不穷, 比如:.Net-Core的EFCore.Sq ...

  8. Java中数据库连接池原理机制的详细讲解以及项目连接数据库采用JDBC常用的几种连接方式

    连接池的基本工作原理 1.基本概念及原理 由上面的分析可以看出,问题的根源就在于对数据库连接资源的低效管理.我们知道,对于共享资源,有一个很著名的设计模式:资源池(Resource Pool).该模式 ...

  9. Unity中对象池的使用

    unity中用到大量重复的物体,例如发射的子弹,可以引入对象池来管理,优化内存. 对象池使用的基本思路是: 将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用.恰当地使用对象池,可以在 ...

随机推荐

  1. SQLServer 使用变量动态行转列

    drop table #testcreate table #test(    id int identity(1,1) primary key,    bizDate varchar(50),    ...

  2. oracle中sum求和问题

    如列表所示:都是选填字段name   age salary weight张三     18      20李四     17王五     21燕小六  15      22 sum(age+salar ...

  3. Azure Service Bus

    Azure Service Bus  是类似Rabbit的一个队列的应用. 找了两个基本的教程 First(但是这个,没有写怎么去链接账户)  Sec:这个有   Third(讲的也很好) Windo ...

  4. 使用cookies查询商品浏览记录

    经历了俩个星期,易买网项目如期完工,现在总结一下如何使用cookies实现浏览商品的历史记录. 第一步:创建商品实体类. 第二步:连接oracle数据库. 第三步:创建商品三层架构. 效果图: 在要显 ...

  5. 图像连通域检测的2路算法Code

    本文算法描述参考链接:http://blog.csdn.net/icvpr/article/details/10259577 两遍扫描法: (1)第一次扫描: 访问当前像素B(x,y),如果B(x,y ...

  6. day37-1 面向对象高阶

    目录 面向对象高阶 isinstance issubclass 反射(自省) 模块的使用 放在类的使用 call 面向对象高阶 isinstance 判断是否为实例化对象,以后可以用来取代type 和 ...

  7. Hadoop 使用小命令(2)

    一.查看一堆文件共有多少行 查看file1/file2目录下所有文件总共多少行 hadoop fs -text file1/file2/* | wc -l 二.正则表达式 hadoop fs -tex ...

  8. eas之新建窗口

    public void actionObjectProp_actionPerformed(ActionEvent e)          throws Exception {     UIContex ...

  9. 绝对好用的浏览器json解析网址

    你们是否经常在浏览器输入请求地址解析遇到中文乱码的情况,今天我找到了一个好用的浏览器解析json网址,绝对好用. 1.直接输入网址 http://pro.jsonlint.com/ 2.输入要解析的j ...

  10. lunix下的redis数据库操作——hash(哈希)

    哈希,形如:key : (field : value) 创建:(可以理解为users用户,name为xxx) hset users name xxx 查看: hget users name # &qu ...