.NET中的各種池

在.NET中,常用到的池有四個:字符串拘留池提揍、線程池 、應(yīng)用程序池煮仇、數(shù)據(jù)庫連接池劳跃。

字符串拘留池

在.NET中字符串是不可變對象,修改字符串變量的值會產(chǎn)生新的對象欺抗。為降低性能消耗及減小程序集大小售碳,.NET提供了string interning的功能,直譯過來就是字符串拘留绞呈。所謂的字符串拘留池(intern pool)其實是一張哈希表贸人,鍵是字符串字面量,值是托管堆上字符串對象的引用佃声。但若該表過大艺智,則會對性能造成負(fù)面影響。在加載程序集時圾亏,不同版本的CLR對于是否留用程序集元數(shù)據(jù)中的字符串字面量(在編譯時值已確定)不盡相同十拣。但顯式調(diào)用string.Intern方法則會將字符串字面量放入池中。

我們在給string類型變量分配字面量值時志鹃,CLR會先到字符串池中看下有沒有完全相同的字符串(區(qū)分大小寫)夭问,若有則返回對應(yīng)的引用,若無曹铃,則創(chuàng)建新對象并添加到字符串池中返回引用缰趋。但若在運(yùn)行時(如,使用new關(guān)鍵字)來給字符串變量分配值則不會使用字符串池。

C#提供了和字符串池相關(guān)的兩個方法:

//若str不在字符串池中就創(chuàng)建新字符串對象放到池里并返回引用
public staticc String Intern(String str);
//若str不在字符串池中不會創(chuàng)建新字符串對象并返回null
public staticc String IsInterned(String str);

示例代碼如下:

var str = "abc";
var str01 = "abc";
//運(yùn)行時常量
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}");

得到如下結(jié)果:


線程池

一個進(jìn)程中只有一個線程池(MSDN)秘血。另一種說法是味抖,一個CLR中一個線程池(《CLR via C#》),我認(rèn)同這種說法灰粮。一個進(jìn)程可以加載多個不同版本的CLR仔涩,但同一版本的CLR只能有一個≌持郏總之熔脂,線程不屬于應(yīng)用程序域(AppDomain)。

若線程池中的線程存在未處理的異常蓖乘,則會導(dǎo)致當(dāng)前進(jìn)程被終止锤悄,但有三個例外:

在.NET1.0和1.1版本中, CLR會處理掉線程池中未處理的異常零聚。但這樣做會破壞應(yīng)用程序中的狀態(tài)甚至導(dǎo)致程序掛起,這些不利于調(diào)試些侍。

在.NET中隶症,許多場景可以使用線程池。如岗宣,異步I/O蚂会,回調(diào),注冊wait操作耗式,使用委托的異步方法調(diào)用及System.Net 中的socket連接胁住。

但在如下場景中應(yīng)避免使用線程池中的線程:

  • 需要使用前臺線程時
  • 線程需要特定優(yōu)先級時
  • 需要執(zhí)行比較耗時的操作時。因為線程池中的線程數(shù)有上限刊咳,因此長時間的阻塞可能會影響其它任務(wù)的處理
  • 當(dāng)需要放置線程在單線程單元(single-threaded apartment)時彪见。線程池中的線程均在多線程單元(multithreaded apartment)中
  • 需要給線程一個穩(wěn)定的標(biāo)識或者線程用于特定任務(wù)時

線程池中的線程分為兩種:工作線程(Worker)I/O線程(I/O Completion Port)這兩種線程只是用處不同娱挨,并無本質(zhì)區(qū)別余指。

線程池中的最小線程數(shù)默認(rèn)為處理器的邏輯核心數(shù)。即跷坝,在4核計算機(jī)上酵镜,線程池中工作線程和I/O線程默認(rèn)的最小數(shù)均為4。理論上柴钻,線程池中的最大線程數(shù)只受可用內(nèi)存大小限制淮韭,但是線程池會限制進(jìn)程內(nèi)可用線程的數(shù)量。

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}");

運(yùn)算結(jié)果如下:


當(dāng)應(yīng)用使用線程池中的線程進(jìn)行工作時贴届,若線程池中沒有線程靠粪,則會創(chuàng)建新的線程以滿足需要足丢,當(dāng)線程池中的線程數(shù)達(dá)到設(shè)定的最小線程數(shù)且無空閑線程時,則會先等待一段時間(最多500ms)庇配,500ms過后依然沒有空閑線程可供使用則會創(chuàng)建新線程進(jìn)行工作,但線程池中的線程數(shù)不會超過設(shè)定的最大線程數(shù)绍些。

當(dāng)線程池中的線程處于空閑狀態(tài)一段時間后(不同CLR捞慌,這個時間不同),會被銷毀柬批。

當(dāng)應(yīng)用負(fù)載較低時啸澡,線程池中的線程數(shù)也有可能小于設(shè)定的最小線程數(shù)。

machine.config中線程池配置如下(.NET 配置文件體系參見:ASP.NET Configuration File Hierarchy and Inheritance):

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

配置線程池大械省:

//這種配置方式和處理CPU邏輯核心數(shù)無關(guān)
ThreadPool.SetMaxThreads(1000, 800);
ThreadPool.SetMinThreads(20, 20);

ASP.NET也可通過配置文件進(jìn)行配置嗅虏,這種方式是針對每個CPU邏輯核心進(jìn)行配置:

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

這樣做,在應(yīng)用啟動后會報錯:在 machine.config 文件之外使用注冊為 allowDefinition='MachineOnly' 的節(jié)是錯誤的上沐。需要修改machine.config文件皮服。

線程池配置得當(dāng)對于應(yīng)用性能提升是有不少幫助的

應(yīng)用程序池

IIS5中参咙,一臺服務(wù)器只有一個工作進(jìn)程龄广,不同應(yīng)用使用AppDomain進(jìn)行區(qū)分,當(dāng)工作進(jìn)程出現(xiàn)問題蕴侧,所有應(yīng)用都會受到影響择同。從IIS6開始引入了應(yīng)用程序池的概念,應(yīng)用程序池通過進(jìn)程來隔離不同的應(yīng)用程序以防止不同應(yīng)用之間相互影響净宵。在部署ASP.NET應(yīng)用時敲才,應(yīng)用程序池通常有兩種托管管道模式可供選擇:集成模式和經(jīng)典模式

默認(rèn)情況下择葡,一個應(yīng)用程序池有一個工作進(jìn)程紧武,可以根據(jù)實際情況設(shè)置多個工作進(jìn)程,但要考慮資源消耗及本地緩存同步問題刁岸。

IIS6和IIS5中的工作進(jìn)程隔離均是在服務(wù)器級別缚陷。在同一臺服務(wù)器上無法使用不同的工作進(jìn)程隔離模式处面。從IIS7開始,工作進(jìn)程隔離模式是基于應(yīng)用程序池的,這樣就可以在同一臺服務(wù)器上使用不同的隔離模式祝旷。

在應(yīng)用程序池——高級設(shè)置中可以對應(yīng)用程序池做相關(guān)設(shè)置,如隊列長度贪婉,工作進(jìn)程回收機(jī)制等户辱。

數(shù)據(jù)庫連接池

和數(shù)據(jù)庫服務(wù)器建立連接的過程是比較耗時的,對此疏哗,ADO.NET中使用了連接池來進(jìn)行優(yōu)化呛讲。在.NET中不同的Data Provider對于連接池的處理方式不盡相同。默認(rèn)情況下,ADO.NET 啟用連接池優(yōu)化贝搁,可以通過連接字符串來配置是否啟用連接池吗氏。

連接池可以減少和數(shù)據(jù)庫建立連接的次數(shù),連接池中維護(hù)著一組活躍的數(shù)據(jù)庫連接雷逆。在我們調(diào)用IDbConnectionOpen方法時弦讽,CLR會去連接池中尋找是否有可用的連接,若有則返回該連接而無需與數(shù)據(jù)庫建立新的連接膀哲。當(dāng)我們調(diào)用IDbConnectionClose方法時往产,連接會被連接池回收但不斷開與數(shù)據(jù)庫的連接,以備下次使用某宪。連接池中的連接空閑一段時間(約4~8分鐘)后或者連接池檢測到連接已與服務(wù)器斷開(需要與服務(wù)器通訊才能檢測連接是否已斷開)仿村,那么該連接將會被銷毀。

在第一次打開連接時兴喂,ADO.NET會根據(jù)連接配置來建立連接池蔼囊。ADO.NET為每個連接配置創(chuàng)建一個連接池,所以若程序中用到多個不同的連接配置(如瞻想,不同的連接字符串)压真,則會有多個連接池。

若連接池中發(fā)生了超時或者其它登錄錯誤蘑险,則會拋出異常滴肿,那么在接下來的5s內(nèi)嘗試該連接都將失敗,這5s鐘成為阻塞期佃迄。若阻塞期結(jié)束后的連接再次失敗泼差,則會進(jìn)入一個新的阻塞期,新的阻塞期時長是上個阻塞期時長的2倍呵俏,但最多不超過1分鐘堆缘。

如果連接字符串中沒有設(shè)置MinPoolSize的值,或者將該值設(shè)為0普碎,那么當(dāng)池中沒有活動連接時吼肥,連接池也會被銷毀。但若將MinPoolSize的值設(shè)為大于0麻车,那么只有在卸載AppDomain時缀皱,連接池才會被銷毀。當(dāng)連接池中發(fā)生了較為嚴(yán)重的錯誤动猬,連接池也會自我清理啤斗。

連接池中最大連接數(shù)默認(rèn)為100,當(dāng)連接池中連接數(shù)已達(dá)到上限赁咙,且均被占用钮莲,那么新的請求會進(jìn)入隊列等到免钻,等待時間超過15s(默認(rèn))則會拋出異常。

數(shù)據(jù)庫連接推薦使用如下寫法崔拥,這樣using語句結(jié)束后极舔,連接對象會回到連接池中以便下次請求使用。

using (IDbConnection conn = new SqlConnection())
{

}

除了客戶端維持的連接池外链瓦,數(shù)據(jù)庫服務(wù)本身還有連接數(shù)的限制

SQL Server最大連接數(shù)限制

我將數(shù)據(jù)庫最大并發(fā)連接數(shù)設(shè)為1姆怪,ADO.NET連接池使用默認(rèn)配置,循環(huán)打開3000個連接并未發(fā)生異常:

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

但若將連接池最大連接數(shù)設(shè)置成一個比較小的數(shù)澡绩,如3,再次執(zhí)行上述代碼則會發(fā)生異常:


按這種情況看俺附,連接池中的連接公用了同一個數(shù)據(jù)庫連接肥卡。在連接池連接數(shù)耗盡時,則因為等待超時而拋出異常事镣。

但我并沒有悟出數(shù)據(jù)庫最大并發(fā)連接數(shù)的奧妙步鉴,哪位童鞋知道,麻煩告知璃哟,在此表示感謝氛琢!

結(jié)語

以上,是本人學(xué)習(xí)的一點兒心得随闪,錯誤之處望大家多多指教阳似。

推薦閱讀

Thread Pool
Exceptions in Managed Threads.
StackExchange.Redis Timeout
記5.28大促壓測的性能優(yōu)化—線程池相關(guān)問題(線程池配置不當(dāng)導(dǎo)致)
工作者線程(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最大連接數(shù)和隊列長度
System.Threading.Tasks.Task引起的IIS應(yīng)用程序池崩潰
HTTP.SYS 詳解
IIS執(zhí)行原理
IIS ASP.NET的進(jìn)程模式淺析


SQL Server Connection Pooling (ADO.NET)
Connection Pooling
SqlServer數(shù)據(jù)庫連接數(shù)與客戶端連接池關(guān)系測試(一)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市铐伴,隨后出現(xiàn)的幾起案子撮奏,更是在濱河造成了極大的恐慌,老刑警劉巖当宴,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件畜吊,死亡現(xiàn)場離奇詭異,居然都是意外死亡户矢,警方通過查閱死者的電腦和手機(jī)玲献,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梯浪,“玉大人捌年,你說我怎么就攤上這事∏ぃ” “怎么了延窜?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長抹锄。 經(jīng)常有香客問我逆瑞,道長荠藤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任获高,我火速辦了婚禮哈肖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘念秧。我一直安慰自己淤井,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布摊趾。 她就那樣靜靜地躺著币狠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪砾层。 梳的紋絲不亂的頭發(fā)上漩绵,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天,我揣著相機(jī)與錄音肛炮,去河邊找鬼止吐。 笑死,一個胖子當(dāng)著我的面吹牛侨糟,可吹牛的內(nèi)容都是我干的碍扔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼秕重,長吁一口氣:“原來是場噩夢啊……” “哼不同!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起溶耘,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤套鹅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后汰具,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卓鹿,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年留荔,在試婚紗的時候發(fā)現(xiàn)自己被綠了吟孙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡聚蝶,死狀恐怖杰妓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情碘勉,我是刑警寧澤巷挥,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站验靡,受9級特大地震影響倍宾,放射性物質(zhì)發(fā)生泄漏雏节。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一高职、第九天 我趴在偏房一處隱蔽的房頂上張望钩乍。 院中可真熱鬧,春花似錦怔锌、人聲如沸寥粹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涝涤。三九已至,卻和暖如春岛杀,著一層夾襖步出監(jiān)牢的瞬間妄痪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工楞件, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人裳瘪。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓土浸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親彭羹。 傳聞我的和親對象是個殘疾皇子黄伊,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容