NET中的池

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

配置线程池大小:

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

这样做,在应用启动后会报错:在 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

版权声明

原文地址:https://www.cnblogs.com/Leo_wl/p/8970048.html

时间: 2024-10-11 04:08:47

NET中的池的相关文章

关于Java中线程池的解读

之前的面试中多次被问到线程池的相关内容,所以在之后的时间内我仔细的学习了一下线程池的相关内容. 1.使用线程池的意义 复用:类似WEB服务器等系统,长期来看内部需要使用大量的线程处理请求,而单次请求响应时间通常比较短,此时Java基于操作系统的本地调用方式大量的创建和销毁线程本身会成为系统的一个性能瓶颈和资源浪费.若使用线程池技术可以实现工作线程的复用,即一个工作线程创建和销毁的生命周期期间内可以执行处理多个任务,从而总体上降低线程创建和销毁的频率和时间,提升了系统性能. 流控:服务器资源有限,

AsyncTask中线程池调度分析

尊重原创:http://blog.csdn.net/yuanzeyao/article/details/42583215 在Android中,和异步执行相关的两个类就是Handler和AsyncTask,所以Android开发人员对于这两个类是再熟悉不过了,所以这里我不是讲解AsyncTask怎么使用,而是想分析一下AsyncTask中线程池的调度过程,然后简单的介绍一下AsyncTask的源码以及Android3.0前后,AsyncTask中线程池的区别. 在正式学习AsyncTask中的线程

java6,7,8中字符串池的进化史与深度剖析

这篇文章将要讨论 Java 6 中是如何实现 String.intern 方法的,以及这个方法在 Java 7 以及 Java 8 中做了哪些调整. 字符串池 字符串池(有名字符串标准化)是通过使用唯一的共享 String 对象来使用相同的值不同的地址表示字符串的过程.你可以使用自己定义的 Map<String, String> (根据需要使用 weak 引用或者 soft 引用)并使用 map 中的值作为标准值来实现这个目标,或者你也可以使用 JDK 提供的 String.intern().

java中线程池

在jdk1.5中加入了java.util.concurrent,这个包中主要介绍java中线程以及线程池的使用 现在看怎么创建一个线程池,在java中Executors类创建线程池的,而线程池分为三类, 1:Executors.newFixedThreadPool(3);//这是固定的线程池 2: Executors.newCachedThreadPool();创建一个带缓存的线程池,比如创建了3个线程在线程池中,但是现在有10个线程在执行任务,因此它还会创建7个线程 3:Executors.n

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

当Java处理高并发的时候,线程数量特别的多的时候,而且每个线程都是执行很短的时间就结束了,频繁创建线程和销毁线程需要占用很多系统的资源和时间,会降低系统的工作效率. 参考http://www.cnblogs.com/dolphin0520/p/3932921.html 由于原文作者使用的API 是1.6 版本的,参考他的文章,做了一些修改成 jdk 1.8版本的方法,涉及到的内容比较多,可能有少许错误. API : jdk1.8.0_144 ThreadPoolExecutor类 Java中线

Unity3D中对象池的实现

在Unity中常常会遇到需要重复创建,销毁某些物体的情况,比如fps类游戏中的子弹,rpg类游戏中的小怪等等,如果直接使用Instantiate和Destroy的话,会浪费系统的资源,而使用对象池则能够节省下这些浪费. 这里使用一个重复利用子弹的进行发射的简单场景来演示对象池. 首先需要一个在场景中创建一个Cube,充当子弹. 然后在Assets目录下创建Resources文件夹,在Cube上添加上刚体,取消重力后,将Cube拖入Resources文件夹内作为一个预设,在场景中删除这个Cube.

python3中线程池

1.在使用多线程处理任务时也不是线程越多越好,由于在切换线程的时候,需要切换上下文环境,依然会造成cpu的大量开销.为解决这个问题,线程池的概念被提出来了.预先创建好一个较为优化的数量的线程,让过来的任务立刻能够使用,就形成了线程池.在python中,没有内置的较好的线程池模块,需要自己实现或使用第三方模块.下面是一个简单的线程池: import threading,time,os,queue class ThreadPool(object): def __init__(self,maxsize

JAVA中线程池的简单使用

比如现在有10个线程,但每次只想运行3个线程,当这3个线程中的任何一个运行完后,第4个线程接着补上.这种情况可以使用线程池来解决,线程池用起来也相当的简单,不信,你看: package com.demo; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public

Unity中对象池的使用

unity中用到大量重复的物体,例如发射的子弹,可以引入对象池来管理,优化内存. 对象池使用的基本思路是: 将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用.恰当地使用对象池,可以在一定程度上减少频繁创建对象所造成的开销. 并非所有对象都适合拿来池化――因为维护对象池也要造成一定开销.对生成时开销不大的对象进行池化,反而可能会出现“维护对象池的开销”大于“生成新对象的开销”,从而使性能降低的情况. GameObjectPool.cs如下 using UnityEngine; u