成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

淺談CLR線程池的缺點(diǎn)及解決方法

開發(fā) 后端
關(guān)于CLR線程池的基本概念我們還不太清楚,這里筆者再來(lái)補(bǔ)充一些必要的信息,有助于我們?cè)诔绦蛑羞x擇合適的使用方式。這里將談到獨(dú)立線程池與IO線程池。

獨(dú)立線程池

上次我們?cè)?A target=_blank>CLR線程池的作用與原理淺析一文中討論到,在一個(gè).NET應(yīng)用程序中會(huì)有一個(gè)CLR線程池,可以使用ThreadPool類中的靜態(tài)方法來(lái)使用這個(gè)線程池。我們只要使用QueueUserWorkItem方法向線程池中添加任務(wù),線程池就會(huì)負(fù)責(zé)在合適的時(shí)候執(zhí)行它們。我們還討論了CLR線程池的一些高級(jí)特性,例如對(duì)線程的最大和最小數(shù)量作限制,對(duì)線程創(chuàng)建時(shí)間作限制以避免突發(fā)的大量任務(wù)消耗太多資源等等。

那么.NET提供的線程池又有什么缺點(diǎn)呢?有些朋友說,一個(gè)重要的缺點(diǎn)就是功能太簡(jiǎn)單,例如只有一個(gè)隊(duì)列,沒法做到對(duì)多個(gè)隊(duì)列作輪詢,無(wú)法取消任務(wù),無(wú)法設(shè)定任務(wù)優(yōu)先級(jí),無(wú)法限制任務(wù)執(zhí)行速度等等。不過其實(shí)這些簡(jiǎn)單的功能,倒都可以通過在CLR線程池上增加一層(或者說,通過封裝CLR線程池)來(lái)實(shí)現(xiàn)。例如,您可以讓放入CLR線程池中的任務(wù),在執(zhí)行時(shí)從幾個(gè)自定義任務(wù)隊(duì)列中挑選一個(gè)運(yùn)行,這樣便達(dá)到了對(duì)多個(gè)隊(duì)列作輪詢的效果。因此,在我看來(lái),CLR線程池的主要缺點(diǎn)并不在此。

我認(rèn)為,CLR線程池的主要問題在于“大一統(tǒng)”,也就是說,整個(gè)進(jìn)程內(nèi)部幾乎所有的任務(wù)都會(huì)依賴這個(gè)線程池。如前篇文章所說的那樣,如Timer和WaitForSingleObject,還有委托的異步調(diào)用,.NET框架中的許多功能都依賴這個(gè)線程池。這個(gè)做法是合適的,但是由于開發(fā)人員對(duì)于統(tǒng)一的線程池?zé)o法做到精確控制,因此在一些特別的需要就無(wú)法滿足了。舉個(gè)最常見例子:控制運(yùn)算能力。什么是運(yùn)算能力?那么還是從線程講起吧。

我們?cè)谝粋€(gè)程序中創(chuàng)建一個(gè)線程,安排給它一個(gè)任務(wù),便交由操作系統(tǒng)來(lái)調(diào)度執(zhí)行。操作系統(tǒng)會(huì)管理系統(tǒng)中所有的線程,并且使用一定的方式進(jìn)行調(diào)度。什么是“調(diào)度”?調(diào)度便是控制線程的狀態(tài):執(zhí)行,等待等等。我們都知道,從理論上來(lái)說有多少個(gè)處理單元(如2 * 2 CPU的機(jī)器便有4個(gè)處理單元),就表示操作系統(tǒng)可以同時(shí)做幾件事情。但是線程的數(shù)量會(huì)遠(yuǎn)遠(yuǎn)超過處理單元的數(shù)量,因此操作系統(tǒng)為了保證每個(gè)線程都被執(zhí)行,就必須等一個(gè)線程在某個(gè)處理器上執(zhí)行到某個(gè)情況的時(shí)候,“換”一個(gè)新的線程來(lái)執(zhí)行,這便是所謂的“上下文切換(context switch)”。至于造成上下文切換的原因也有多種,可能是某個(gè)線程的邏輯決定的,如遇上鎖,或主動(dòng)進(jìn)入休眠狀態(tài)(調(diào)用Thread.Sleep方法),但更有可能是操作系統(tǒng)發(fā)現(xiàn)這個(gè)線程“超時(shí)”了。在操作系統(tǒng)中會(huì)定義一個(gè)“時(shí)間片(timeslice)”2,當(dāng)發(fā)現(xiàn)一個(gè)線程執(zhí)行時(shí)間超過這個(gè)時(shí)間,便會(huì)把它撤下,換上另外一個(gè)。這樣看起來(lái),多個(gè)線程——也就是多個(gè)任務(wù)在同時(shí)運(yùn)行了。值得一提的是,對(duì)于Windows操作系統(tǒng)來(lái)說,它的調(diào)度單元是線程,這和線程究竟屬于哪個(gè)進(jìn)程并沒有關(guān)系。

舉個(gè)例子,如果系統(tǒng)中只有兩個(gè)進(jìn)程,進(jìn)程A有5個(gè)線程,而進(jìn)程B有10個(gè)線程。在排除其他因素的情況下,進(jìn)程B占有運(yùn)算單元的時(shí)間便是進(jìn)程A的兩倍。當(dāng)然,實(shí)際情況自然不會(huì)那么簡(jiǎn)單。例如不同進(jìn)程會(huì)有不同的優(yōu)先級(jí),線程相對(duì)于自己所屬的進(jìn)程還會(huì)有個(gè)優(yōu)先級(jí);如果一個(gè)線程在許久沒有執(zhí)行的時(shí)候,或者這個(gè)線程剛從“鎖”的等待中恢復(fù),操作系統(tǒng)還會(huì)對(duì)這個(gè)線程的優(yōu)先級(jí)作臨時(shí)的提升——這一切都是牽涉到程序的運(yùn)行狀態(tài),性能等情況的因素,有機(jī)會(huì)我們?cè)谧稣归_。

現(xiàn)在您意識(shí)到線程數(shù)量意味著什么了沒?沒錯(cuò),就是我們剛才提到的“運(yùn)算能力”。很多時(shí)候我們可以簡(jiǎn)單的認(rèn)為,在同樣的環(huán)境下,一個(gè)任務(wù)使用的線程數(shù)量越多,它所獲得的運(yùn)算能力就比另一個(gè)線程數(shù)量較少的任務(wù)要來(lái)得多。運(yùn)算能力自然就涉及到任務(wù)執(zhí)行的快慢。您可以設(shè)想一下,有一個(gè)生產(chǎn)任務(wù),和一個(gè)消費(fèi)任務(wù),它們使用一個(gè)隊(duì)列做臨時(shí)存儲(chǔ)。在理想情況下,生產(chǎn)和消費(fèi)的速度應(yīng)該保持相同,這樣可以帶來(lái)最好的吞吐量。如果生產(chǎn)任務(wù)執(zhí)行較快,則隊(duì)列中便會(huì)產(chǎn)生堆積,反之消費(fèi)任務(wù)就會(huì)不斷等待,吞吐量也會(huì)下降。因此,在實(shí)現(xiàn)的時(shí)候,我們往往會(huì)為生產(chǎn)任務(wù)和消費(fèi)任務(wù)分別指派獨(dú)立的線程池,并且通過增加或減少線程池內(nèi)線程數(shù)量來(lái)?xiàng)l件運(yùn)算能力,使生產(chǎn)和消費(fèi)的步調(diào)達(dá)到平衡。

使用獨(dú)立的線程池來(lái)控制運(yùn)算能力的做法很常見,一個(gè)典型的案例便是SEDA架構(gòu):整個(gè)架構(gòu)由多個(gè)Stage連接而成,每個(gè)Stage均由一個(gè)隊(duì)列和一個(gè)獨(dú)立的線程池組成,調(diào)節(jié)器會(huì)根據(jù)隊(duì)列中任務(wù)的數(shù)量來(lái)調(diào)節(jié)線程池內(nèi)的線程數(shù)量,最終使應(yīng)用程序獲得優(yōu)異的并發(fā)能力。

在Windows操作系統(tǒng)中,Server 2003及之前版本的API也只提供了進(jìn)程內(nèi)部單一的線程池,不過在Vista及Server 2008的API中,除了改進(jìn)線程池的性能之外,還提供了在同一進(jìn)程內(nèi)創(chuàng)建多個(gè)線程池的接口。很可惜,.NET直到如今的4.0版本,依舊沒有提供構(gòu)建獨(dú)立線程池的功能。構(gòu)造一個(gè)優(yōu)秀的線程池是一件相當(dāng)困難的事情,幸運(yùn)的是,如果我們需要這方面的功能,可以借助著名的SmartThreadPool,經(jīng)過那么多年的考驗(yàn),相信它已經(jīng)足夠成熟了。如果需要,我們還可以對(duì)它做一定修改——畢竟在不同情況下,我們對(duì)線程池的要求也不完全相同。

IO線程池

IO線程池便是為異步IO服務(wù)的線程池。

訪問IO最簡(jiǎn)單的方式(如讀取一個(gè)文件)便是阻塞的,代碼會(huì)等待IO操作成功(或失敗)之后才繼續(xù)執(zhí)行下去,一切都是順序的。但是,阻塞式IO有很多缺點(diǎn),例如讓UI停止響應(yīng),造成上下文切換,CPU中的緩存也可能被清除甚至內(nèi)存被交換到磁盤中去,這些都是明顯影響性能的做法。此外,每個(gè)IO都占用一個(gè)線程,容易導(dǎo)致系統(tǒng)中線程數(shù)量很多,最終限制了應(yīng)用程序的伸縮性。因此,我們會(huì)使用“異步IO”這種做法。

在使用異步IO時(shí),訪問IO的線程不會(huì)被阻塞,邏輯將會(huì)繼續(xù)下去。操作系統(tǒng)會(huì)負(fù)責(zé)把結(jié)果通過某種方法通知我們,一般說來(lái),這種方式是“回調(diào)函數(shù)”。異步IO在執(zhí)行過程中是不占用應(yīng)用程序的線程的,因此我們可以用少量的線程發(fā)起大量的IO,所以應(yīng)用程序的響應(yīng)能力也可以有所提高。此外,同時(shí)發(fā)起大量IO操作在某些時(shí)候會(huì)有額外的性能優(yōu)勢(shì),例如磁盤和網(wǎng)絡(luò)可以同時(shí)工作而不互相沖突,磁盤還可以根據(jù)磁頭的位置來(lái)訪問就近的數(shù)據(jù),而不是根據(jù)請(qǐng)求的順序進(jìn)行數(shù)據(jù)讀取,這樣可以有效減少磁頭的移動(dòng)距離。

Windows操作系統(tǒng)中有多種異步IO方式,但是性能最高,伸縮性最好的方式莫過于傳說中的“IO完成端口(I/O Completion Port,IOCP)”了,這也是.NET中封裝的唯一異步IO方式。大約一年半前,老趙寫過一篇文章《正確使用異步操作》,其中除了描述計(jì)算密集型和IO密集型操作的區(qū)別和效果之外,還簡(jiǎn)單地講述了IOCP與CLR交互的方式,摘錄如下:

當(dāng)我們希望進(jìn)行一個(gè)異步的IO-Bound Operation時(shí),CLR會(huì)(通過Windows API)發(fā)出一個(gè)IRP(I/O Request Packet)。當(dāng)設(shè)備準(zhǔn)備妥當(dāng),就會(huì)找出一個(gè)它“最想處理”的IRP(例如一個(gè)讀取離當(dāng)前磁頭最近的數(shù)據(jù)的請(qǐng)求)并進(jìn)行處理,處理完畢后設(shè)備將會(huì)(通過Windows)交還一個(gè)表示工作完成的IRP。CLR會(huì)為每個(gè)進(jìn)程創(chuàng)建一個(gè)IOCP(I/O Completion Port)并和Windows操作系統(tǒng)一起維護(hù)。IOCP中一旦被放入表示完成的IRP之后(通過內(nèi)部的ThreadPool.BindHandle完成),CLR就會(huì)盡快分配一個(gè)可用的線程用于繼續(xù)接下去的任務(wù)。
不過事實(shí)上,使用Windows API編寫IOCP非常復(fù)雜。而在.NET中,由于需要迎合標(biāo)準(zhǔn)的APM(異步編程模型),在使用方便的同時(shí)也放棄一定的控制能力。因此,在一些真正需要高吞吐量的時(shí)候(如編寫服務(wù)器),不少開發(fā)人員還是會(huì)選擇直接使用Native Code編寫相關(guān)代碼。不過在絕大部分的情況下,.NET中利用IOCP的異步IO操作已經(jīng)足以獲得非常優(yōu)秀的性能了。使用APM方式在.NET中使用異步IO非常簡(jiǎn)單,如下:

  1. static void Main(string[] args)  
  2. {  
  3.     WebRequest request = HttpWebRequest.Create("http://www.cnblogs.com");  
  4.     request.BeginGetResponse(HandleAsyncCallback, request);  
  5. }  
  6.  
  7. static void HandleAsyncCallback(IAsyncResult ar)  
  8. {  
  9.     WebRequest request = (WebRequest)ar.AsyncState;  
  10.     WebResponse response = request.EndGetResponse(ar);  
  11.     // more operations...  
  12. }

BeginGetResponse 將發(fā)起一個(gè)利用IOCP的異步IO操作,并在結(jié)束時(shí)調(diào)用HandleAsyncCallback回調(diào)函數(shù)。那么,這個(gè)回調(diào)函數(shù)是由哪里的線程執(zhí)行的呢?沒錯(cuò),就是傳說中“IO線程池”的線程。.NET在一個(gè)進(jìn)程中準(zhǔn)備了兩個(gè)線程池,除了上篇文章中所提到的CLR線程池之外,它還為異步IO操作的回調(diào)準(zhǔn)備了一個(gè)IO線程池。IO線程池的特性與CLR線程池類似,也會(huì)動(dòng)態(tài)地創(chuàng)建和銷毀線程,并且也擁有最大值和最小值(可以參考上一篇文章列舉出的API)。

只可惜,IO線程池也僅僅是那“一整個(gè)”線程池,CLR線程池的缺點(diǎn)IO線程池也一應(yīng)俱全。例如,在使用異步IO方式讀取了一段文本之后,下一步操作往往是對(duì)其進(jìn)行分析,這就進(jìn)入了計(jì)算密集型操作了。但對(duì)于計(jì)算密集型操作來(lái)說,如果使用整個(gè)IO線程池來(lái)執(zhí)行,我們無(wú)法有效的控制某項(xiàng)任務(wù)的運(yùn)算能力。因此在有些時(shí)候,我們?cè)诨卣{(diào)函數(shù)內(nèi)部會(huì)把計(jì)算任務(wù)再次交還給獨(dú)立的線程池。這么做從理論上看會(huì)增大線程調(diào)度的開銷,不過實(shí)際情況還得看具體的評(píng)測(cè)數(shù)據(jù)。如果它真的成為影響性能的關(guān)鍵因素之一,我們就可能需要使用Native Code來(lái)調(diào)用IOCP相關(guān)API,將回調(diào)任務(wù)直接交給獨(dú)立的線程池去執(zhí)行了。

我們也可以使用代碼來(lái)操作IO線程池,例如下面這個(gè)接口便是向IO線程池遞交一個(gè)任務(wù):

  1. public static class ThreadPool  
  2. {  
  3.     public static bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped);  

NativeOverlapped包含了一個(gè)IOCompletionCallback回調(diào)函數(shù)及一個(gè)緩沖對(duì)象,可以通過Overlapped對(duì)象創(chuàng)建

Overlapped會(huì)包含一個(gè)被固定的空間,這里“固定”的含義表示不會(huì)因?yàn)镚C而導(dǎo)致地址改變,甚至不會(huì)被置換到硬盤上的Swap空間去。這么做的目的是迎合IOCP的要求,但是很明顯它也會(huì)降低程序性能。因此,我們?cè)趯?shí)際編程中幾乎不會(huì)使用這個(gè)方法3。

注1:如果沒有加以說明,我們這里談?wù)摰膶?duì)象默認(rèn)為XP及以上版本的Window操作系統(tǒng)。

注2:timeslice又被稱為quantum,不同操作系統(tǒng)中定義的這個(gè)值并不相同。在Windows客戶端操作系統(tǒng)(XP,Vista)中時(shí)間片默認(rèn)為2個(gè)clock interval,在服務(wù)器操作系統(tǒng)(2003,2008)中默認(rèn)為12個(gè)clock interval(在主流系統(tǒng)上,1個(gè)clock interval大約10到15毫秒)。服務(wù)器操作系統(tǒng)使用較長(zhǎng)的時(shí)間片,是因?yàn)橐话惴?wù)器上運(yùn)行的程序比客戶端要少很多,且更注重性能和吞吐量,而客戶端系統(tǒng)更注重響應(yīng)能力——而且,如果您真需要的話,時(shí)間片的長(zhǎng)度也是可以調(diào)整的。

注3:不過,如果程序中多次復(fù)用單個(gè)NativeOverlapped對(duì)象的話,這個(gè)方法的性能會(huì)略微好于QueueUserWorkItem,據(jù)說WCF中便使用了這種方式——微軟內(nèi)部總有那么些技巧是我們不知如何使用的,例如老趙記得之前查看ASP.NET AJAX源代碼的時(shí)候,在MSDN中不小心發(fā)現(xiàn)一個(gè)接口描述大意是“預(yù)留方法,請(qǐng)不要在外部使用”。對(duì)此,我們又能有什么辦法呢?

【編輯推薦】

  1. CLR線程池的作用與原理淺析
  2. 一個(gè)非常簡(jiǎn)單和短小的線程池
  3. Java學(xué)習(xí):線程池的簡(jiǎn)單構(gòu)建
  4. 創(chuàng)建Java中的線程池
  5. 線程池與工作隊(duì)列
責(zé)任編輯:彭凡 來(lái)源: cnblogs
相關(guān)推薦

2009-07-22 09:39:18

CLR線程池

2009-06-17 15:33:50

java heap s

2009-07-03 18:14:27

Servlet線程安全

2009-07-09 17:14:11

Incompatibl

2010-07-22 14:05:33

krb5-telnet

2009-07-01 18:14:36

JSP亂碼

2023-10-26 08:16:20

C++線程

2009-07-10 14:32:06

JVM崩潰

2011-05-06 17:25:58

硒鼓

2010-06-21 09:54:50

Linux Aplay

2011-04-29 13:22:48

ThinkPad筆記本故障

2009-09-18 11:29:23

.NET CLR

2010-08-12 09:30:08

Flex內(nèi)存泄露

2022-04-02 20:27:30

ETS操作系統(tǒng)鴻蒙

2009-07-15 16:14:36

iBATIS優(yōu)缺點(diǎn)

2012-05-15 02:18:31

Java線程池

2010-12-27 10:48:10

VirtualboxFreedos

2022-04-06 10:09:17

云服務(wù)云計(jì)算

2011-05-18 14:00:30

在線備份

2009-10-22 10:06:54

CLR線程池教程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 欧美精品video | 亚洲人人 | 日韩精品成人在线 | 免费成人高清在线视频 | 欧美中文字幕在线观看 | 在线国产视频 | 亚洲精品粉嫩美女一区 | 日本色婷婷 | 日本 欧美 三级 高清 视频 | 黄色一级毛片免费看 | 美国一级毛片a | 亚洲在线一区 | 米奇7777狠狠狠狠视频 | 欧美日韩一区二区三区四区五区 | 北条麻妃视频在线观看 | 黄色免费av | 日韩欧美在线一区 | 九九久久在线看 | 成人免费小视频 | 91精品国产综合久久久密闭 | 无码一区二区三区视频 | 亚洲精品一区二区二区 | 亚洲美女av网站 | 毛片日韩 | 91久久久久 | 久久性 | 欧美在线激情 | 亚洲综合久久久 | 一级毛片免费完整视频 | 北条麻妃国产九九九精品小说 | 成人精品国产一区二区4080 | 国产福利视频在线观看 | 伊人久久成人 | 久久精品久久精品 | 一区二区三区国产在线观看 | 国产成人久久精品 | 亚洲+变态+欧美+另类+精品 | 亚洲三级在线观看 | 人操人人 | 日韩精品在线看 | xx视频在线 |