簡介
執行緒池(英語:thread pool):一種
執行緒使用模式。執行緒過多會帶來調度開銷,進而影響快取局部性和整體性能。而執行緒池維護著多個執行緒,等待著監督管理者分配可並發執行的任務。這避免了在處理短時間任務時創建與銷毀執行緒的代價。執行緒池不僅能夠保證核心的充分利用,還能防止過分調度。可用執行緒數量應該取決於可用的並發處理器、處理器核心、記憶體、網路sockets等的數量。 例如,執行緒數一般取cpu數量+2比較合適,執行緒數過多會導致額外的執行緒切換開銷。
任務調度以執行執行緒的常見方法是使用同步佇列,稱作任務佇列。池中的執行緒等待佇列中的任務,並把執行完的任務放入完成佇列中。
執行緒池模式一般分為兩種:HS/HA半同步/半異步模式、L/F領導者與跟隨者模式。
組成部分
伺服器程式利用執行緒技術回響客戶請求已經司空見慣,可能您認為這樣做效率已經很高,但您有沒有想過最佳化一下使用執行緒的方法。該文章將向您介紹伺服器程式如何利用執行緒池來最佳化性能並提供一個簡單的執行緒池實現。
1、執行緒池管理器(ThreadPoolManager):用於創建並管理執行緒池
2、工作執行緒(WorkThread): 執行緒池中執行緒
3、任務接口(Task):每個任務必須實現的接口,以供工作執行緒調度任務的執行。
4、任務佇列:用於存放沒有處理的任務。提供一種緩衝機制。
技術背景
在面向對象編程中,創建和銷毀對象是很費時間的,因為創建一個對象要獲取記憶體資源或者其它更多資源。在Java中更是如此,虛擬機將試圖跟蹤每一個對象,以便能夠在對象銷毀後進行垃圾回收。所以提高服務程式效率的一個手段就是儘可能減少創建和銷毀對象的次數,特別是一些很耗資源的對象創建和銷毀。如何利用已有對象來服務就是一個需要解決的關鍵問題,其實這就是一些"池化資源"技術產生的原因。比如大家所熟悉的資料庫連線池正是遵循這一思想而產生的,本文將介紹的執行緒池技術同樣符合這一思想。
目前,一些著名的大公司都特別看好這項技術,並早已經在他們的產品中套用該技術。比如IBM的WebSphere,IONA的Orbix 2000在SUN的 Jini中,Microsoft的MTS(Microsoft Transaction Server 2.0),COM+等。
功能
應用程式可以有多個執行緒,這些執行緒在
休眠狀態中需要耗費大量時間來等待事件發生。其他
執行緒可能進入睡眠狀態,並且僅定期被喚醒以輪循更改或更新狀態信息,然後再次進入休眠狀態。為了簡化對這些執行緒的管理,.NET框架為每個進程提供了一個執行緒池,一個執行緒池有若干個等待操作狀態,當一個等待操作完成時,執行緒池中的
輔助執行緒會執行
回調函式。執行緒池中的執行緒由系統管理,程式設計師不需要費力於執行緒管理,可以集中精力處理應用程式任務。
傳遞參數
調用QueueUserWorkItem時傳入的Object類型參數傳遞到任務過程,可以通過這種方式來向任務過程傳遞參數。如果任務過程需要多個參數,可以定義包含這些數據的類,並將其
強制轉換為Object數據類型。
套用範圍
1、需要大量的執行緒來完成任務,且完成任務的時間比較短。 WEB伺服器完成網頁請求這樣的任務,使用執行緒池技術是非常合適的。因為單個任務小,而任務數量巨大,你可以想像一個熱門網站的點擊次數。 但對於長時間的任務,比如一個Telnet連線請求,執行緒池的優點就不明顯了。因為Telnet會話時間比執行緒的創建時間大多了。
2、對性能要求苛刻的套用,比如要求伺服器迅速回響客戶請求。
3、接受突發性的大量請求,但不至於使伺服器因此產生大量執行緒的套用。突發性大量客戶請求,在沒有執行緒池情況下,將產生大量執行緒,雖然理論上大部分作業系統執行緒數目最大值不是問題,短時間內產生大量執行緒可能使記憶體到達極限,並出現"OutOfMemory"的錯誤。
示例
執行緒池
//執行緒池示例using System;using System.Threading;public classTest{ //存放要計算的數值的欄位 static double number1 = -1; static double number2 = -1; public static void Main() { //獲取執行緒池的最大執行緒數和維護的最小空閒執行緒數 int maxThreadNum, minThreadNum; int portThreadNum; ThreadPool.GetMaxThreads(out maxThreadNum, out portThreadNum); ThreadPool.GetMinThreads(out minThreadNum, out portThreadNum); Console.WriteLine("最大執行緒數:{0}", maxThreadNum); Console.WriteLine("最小執行緒數:{0}", minThreadNum); //函式變數值 int x=15600; //啟動第一個任務:計算x的8次方 Console.WriteLine("啟動第一個任務:計算{0}的8次方。", x); ThreadPool.QueueUserWorkItem(new WaitCallback(TaskProc1), x); //啟動第二個任務:計算x的8次方根 Console.WriteLine("啟動第二個任務:計算{0}的8次方根。", x); ThreadPool.QueueUserWorkItem(new WaitCallback(TaskProc2), x); //等待,直到兩個數值都完成計算 while(number1 == -1 || number2 == -1); //列印計算結果 Console.WriteLine("y({0}) = {1}", x, number1 + number2); Console.Read(); } //啟動第一個任務:計算x的8次方 static void TaskProc1(object o) { number1 = Math.Pow(Convert.ToDouble(o), 8); } //啟動第二個任務:計算x的8次方根 static void TaskProc2(object o) { number2 = Math.Pow(Convert.ToDouble(o), 1.0/8.0); }}
池結構
[HostProtection(SecurityAction.LinkDemand,Synchronization=true,ExternalThreading=true)]public static class ThreadPool{[Obsolete("ThreadPool.BindHandle(IntPtr)hasbeendeprecated.PleaseuseThreadPool.BindHandle(SafeHandle)instead.",false),SecurityPermission(SecurityAction.Demand,Flags=SecurityPermissionFlag.UnmanagedCode)]public static bool BindHandle(IntPtr osHandle){ if(osHandle == null){throw new ArgumentNullException("osHandle");} bool flag = false; bool success = false; RuntimeHelpers.PrepareConstrainedRegions(); try { osHandle.DangerousAddRef(ref success); flag = BindIOCompletionCallbackNative(osHandle.DangerousGetHandle()); } finally { if(success) osHandle.DangerousRelease(); } return flag;}