執行緒池

執行緒池

執行緒池是一種多執行緒處理形式,處理過程中將任務添加到佇列,然後在創建執行緒後自動啟動這些任務。執行緒池執行緒都是後台執行緒。每個執行緒都使用默認的堆疊大小,以默認的優先權運行,並處於多執行緒單元中。如果某個執行緒在託管代碼中空閒(如正在等待某個事件),則執行緒池將插入另一個輔助執行緒來使所有處理器保持繁忙。如果所有執行緒池執行緒都始終保持繁忙,但佇列中包含掛起的工作,則執行緒池將在一段時間後創建另一個輔助執行緒但執行緒的數目永遠不會超過最大值。超過最大值的執行緒可以排隊,但他們要等到其他執行緒完成後才啟動。

基本介紹

  • 中文名:執行緒池
  • 外文名:thread pool
  • 類別:多執行緒處理形式
  • 適用範圍:網際網路
簡介,組成部分,技術背景,功能,傳遞參數,套用範圍,示例,執行緒池,池結構,

簡介

執行緒池(英語:thread pool):一種執行緒使用模式。執行緒過多會帶來調度開銷,進而影響快取局部性和整體性能。而執行緒池維護著多個執行緒,等待著監督管理者分配可並發執行的任務。這避免了在處理短時間任務時創建與銷毀執行緒的代價。執行緒池不僅能夠保證核心的充分利用,還能防止過分調度。可用執行緒數量應該取決於可用的並發處理器、處理器核心、記憶體、網路sockets等的數量。 例如,執行緒數一般取cpu數量+2比較合適,執行緒數過多會導致額外的執行緒切換開銷。
任務調度以執行執行緒的常見方法是使用同步佇列,稱作任務佇列。池中的執行緒等待佇列中的任務,並把執行完的任務放入完成佇列中。
執行緒池模式一般分為兩種:HS/HA半同步/半異步模式、L/F領導者與跟隨者模式。
  • 半同步/半異步模式又稱為生產者消費者模式,是比較常見的實現方式,比較簡單。分為同步層、佇列層、異步層三層。同步層的主執行緒處理工作任務並存入工作佇列,工作執行緒從工作佇列取出任務進行處理,如果工作佇列為空,則取不到任務的工作執行緒進入掛起狀態。由於執行緒間有數據通信,因此不適於大數據量交換的場合。
  • 領導者跟隨者模式,線上程池中的執行緒可處在3種狀態之一:領導者leader、追隨者follower或工作者processor。任何時刻執行緒池只有一個領導者執行緒。事件到達時,領導者執行緒負責訊息分離,並從處於追隨者執行緒中選出一個來當繼任領導者,然後將自身設定為工作者狀態去處置該事件。處理完畢後工作者執行緒將自身的狀態置為追隨者。這一模式實現複雜,但避免了執行緒間交換任務數據,提高了CPU cache相似性。在ACE(Adaptive Communication Environment)中,提供了領導者跟隨者模式實現。
執行緒池的伸縮性對性能有較大的影響。
  • 創建太多執行緒,將會浪費一定的資源,有些執行緒未被充分使用。
  • 銷毀太多執行緒,將導致之後浪費時間再次創建它們。
  • 創建執行緒太慢,將會導致長時間的等待,性能變差。
  • 銷毀執行緒太慢,導致其它執行緒資源飢餓。

組成部分

伺服器程式利用執行緒技術回響客戶請求已經司空見慣,可能您認為這樣做效率已經很高,但您有沒有想過最佳化一下使用執行緒的方法。該文章將向您介紹伺服器程式如何利用執行緒池來最佳化性能並提供一個簡單的執行緒池實現。
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;}

相關詞條

熱門詞條

聯絡我們