C#多线程之旅(3)——线程池
閱讀目錄
- 代碼下載
- 一、介紹
- 二、通過TPL進入線程池
- 三、不用TPL進入到線程池
v博客前言
先交代下背景,寫《C#多線程之旅》這個系列文章主要是因為以下幾個原因:1.多線程在C/S和B/S架構(gòu)中用得是非常多的;2.而且多線程的使用是非常復雜的,如果沒有用好,容易造成很多問題。
v寫在前面
多線程,有利也有弊,使用需謹慎。
v正文開始
代碼下載
Thread_博客園_cnblogs_jackson0714.zip
第一篇~第三篇的代碼示例:
源碼地址:https://github.com/Jackson0714/Threads
回到頂部
一、介紹
無論你什么時候開始一個線程,幾百毫秒會花在整理一個新的local variable stack。每一個線程默認會消耗1MB的內(nèi)存。線程池通過分享和回收線程來削減這些開銷,允許多線程被應(yīng)用在一個非常顆粒級的級別而沒有性能損失。當充分利用多核系統(tǒng)去執(zhí)行密集型計算的并行代碼時這是非常有用的。
線程池也會在線程的總數(shù)量上保持一個限制,從而使線程能夠更平穩(wěn)地運行。太多的線程將會造成管理負擔和使CPU緩存是小,從而造成操作系統(tǒng)不能運行。一旦一個限制到達,job排隊等待直到另外一個完成才開始。這會使任意的并行應(yīng)用程序成為可能,比如一個web server(同步方法是高級技巧,可以更高效地使用線程池中的線程)。
下面是幾種方式進入線程池:
下面的結(jié)構(gòu)直接使用線程池:
Task Parallel Library(TPL)和PLINQ是充分有效的和高等級的,甚至當線程池是不重要的時候,你也會想使用它們?nèi)f(xié)助處理多線程。
現(xiàn)在我們簡單的看一下我們怎樣使用Task類來實現(xiàn)一個簡單的運行在線程池上的委托。
當使用線程池時需要注意下面的事情:
你不能任意地改變池中的線程的優(yōu)先級-因為當它釋放會池中的時候,優(yōu)先級會被還原為正常狀態(tài)。
你可以通過屬性Thread.CurrentThread.IsThreadPoolThread的屬性查詢線程是否是正在運行的一個池中的線程
回到頂部
二、通過TPL進入線程池
你可以使用在TaskParallel Library中的Task類來輕松的進入線程池。這個Task類在Framework 4.0中有介紹:如果你對老的結(jié)構(gòu)比較熟悉,考慮用非泛型的Task類替換ThreadPool.QueueUserWorkItem,將Asunchoronous?delgates替換為泛型Task<TResult>。最新的結(jié)構(gòu)速度更快,更方便,而且更復雜。
為了使用非泛型的任務(wù)類,調(diào)用Task.Factory.StartNew方法,將方法傳進委托中。
Task.Factory.StartNew會返回一個Task對象,你可以使用它去監(jiān)控這個task,比如,你可以調(diào)用它的wait方法等待它直到它完成。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | static?void?Main(string[] args) { ????Task task =? Task.Factory.StartNew(Go); ????task.Wait(); ????Console.ReadKey(); } ? static?void?Go() { ????Console.WriteLine("From the thread pool start..."); ????Thread.Sleep(3000); ????Console.WriteLine("From the thread pool end"); } |
當你調(diào)用task的Wait?方法時,一個未處理的異常會很容易地重新拋出到宿主線程上。(如果你不調(diào)用Wait方法而是放棄這個task,一個未處理的異常將會關(guān)閉掉這個進程)
泛型Task<Tresult>類是非泛型Task的子類。它讓你從這個已經(jīng)完成執(zhí)行的task中得到一個返回值。在下面的例子中,我們使用Task<TResult>來下載一個web page
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | static?void?Main(string[] args) { ????Task<string> task = Task.Factory.StartNew<string>( ????????() => DownloadString("http://www.baidu.com")); ????//調(diào)用其他方法 ????// ? ????//可以用task的Result的屬性來獲得task返回值。 ????//如果這個任務(wù)還在運行,當前的主線程將會被阻塞,直到這個任務(wù)完成。 ????string?result = task.Result; } ? static?string?DownloadString(string?uri) { ????using(var?wc =?new?System.Net.WebClient()) ????{ ????????return?wc.DownloadString(uri); ????} } |
Task Parallel Library有許多的功能,特別是提升多核處理器的性能。我們會在并行編程中繼續(xù)討論TPL。
回到頂部
三、不用TPL進入到線程池
如果你的應(yīng)用程序是.NET Framework的早期版本(4.0之前的版本),你將不能使用TPL。你必須使用老的結(jié)構(gòu)進入線程池:
ThreadPool.QueueUserWorkItem和asynchoronous?delegates.兩者的不同點是asynchronous?delegates讓你從線程那里返回數(shù)據(jù)。Asynchronous?delegates收集任何exception返回給調(diào)用者。
要使用QueueUserWorkItem,只需調(diào)用這個方法的運行在線程池上的委托。
| 1 2 3 4 5 6 7 8 9 10 11 | static?void?Main(string[] args) { ????ThreadPool.QueueUserWorkItem(Go); ????ThreadPool.QueueUserWorkItem(Go, 123); ????Console.ReadKey(); } ? static?void?Go(object?data) { ????Console.WriteLine("A from thread pool! "?+ data); } |
我們的目標方法Go,必須接收一個簡單object類型的參數(shù)(為了滿足waitCallBack委托)。這將提供一個簡單的方式傳遞數(shù)據(jù)到方法中,就像是ParameterizedThreadStart。不像Task,QueueUserWorkItem不會返回一個對象去幫助你之后管理執(zhí)行。還有,你必須顯式在目標方法的代碼中寫處理異常的代碼-因為未處理的異常將會終止程序。
?ThreadPool.QueueUserWorkItem沒有提供從一個已經(jīng)完成的線程中得到它的返回值的機制。Asynchronous?delegate invocations(asynchronous delegates for short)解決了這個問題,允許任何個數(shù)類型化的參數(shù)在兩個方向傳遞。此外,在asynchronous delegates上未處理的異常很方便地在原始線程上重新拋出(更準確地說,這個線程叫做EndInvoke),因此不需要顯示處理。
不要混淆asynchronous delegates和asynchronous method(方法以Begin和End開頭的,比如File.BeginRead/File.EndRead)。Asynchronous methods表面上按照簡單的協(xié)議,但是它們的存在是為了解決一個更困難的問題。
下面是怎樣通過一個asynchronous delegate開始一個worker task:
在下面的例子中,我們使用一個asynchronous delegate invocation運行一個與主線程同時運行的簡單方法,這個方法返回一個字符串的長度:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | static?void?Main(string[] args) { ????Func<string,?int> t = Go; ????IAsyncResult result = t.BeginInvoke("test",?null,?null); ????// ????// ... 這里可以執(zhí)行其他并行的任務(wù) ????// ? ????int?length = t.EndInvoke(result); ????Console.WriteLine("String lenth is: "?+ length); ????Console.ReadKey(); } ? static?int?Go(string?messsage) { ????return?messsage.Length; } |
EndInvoke做三件事情。第一,如果asynchronous delegate沒有完成執(zhí)行,則一直等待它完成。第二,接收返回值(以及任何ref或者out參數(shù))。第三,返回任何未處理的線程異常給調(diào)用它的線程。
注意:如果你用asynchronous delegate調(diào)用的方法沒有返回值,你在技術(shù)上需要調(diào)用EndInvoke。在實踐中,這是開放的辯論;沒有Endinvoke報警去管理處罰未編譯者!如果你選擇不去調(diào)用EndInvoke,然而,你需要考慮在線程的異常去避免靜默失敗。
當你調(diào)用BeginInvoke方法時,可以指定一個call back delegate-一個可以接收一個IAsyncResult?對象的方法,它會在委托方法完成后被自動調(diào)用這個允許正在發(fā)動的線程忘記asynchronous delegate,但它在call back結(jié)束時需要一點額外的工作。
v寫在最后
線程池的使用的提升還沒有寫,最近兩年作息不規(guī)律,程序員得養(yǎng)好身體,早睡早起,睡覺睡覺。希望這篇博客能幫到大家,希望得到園友們的支持!
作 者:?Jackson0714?
出 處:http://www.cnblogs.com/jackson0714/?
關(guān)于作者:專注于微軟平臺的項目開發(fā)。如有問題或建議,請多多賜教!?
版權(quán)聲明:本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。?
特此聲明:所有評論和私信都會在第一時間回復。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信我?
總結(jié)
以上是生活随笔為你收集整理的C#多线程之旅(3)——线程池的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 连夜抢收小麦的画面好震撼:还有北斗导航加
- 下一篇: C#中双问号、双冒号等几个特殊关键字