1. 程式人生 > >VB.net學習筆記(三十)認識執行緒池

VB.net學習筆記(三十)認識執行緒池

    水是生命之源,計算機資源也一樣。

    每一執行緒尤如一滴水,你花一滴,我花一滴,你還一滴,我還一滴,就象遊兵散將一樣,執行緒越多,越複雜混亂。而每一個執行緒建立需要開銷,活動的執行緒也需要開銷。過多的執行緒導致系統記憶體佔用過度或系統資源不足。為了解決執行緒生命週期開銷問題和資源不足問題,建立執行緒池,讓每滴水(執行緒)納入統一管理。特別是那些生存期比較短暫的執行緒。使用執行緒池執行任務比每次完成一個任務時都建立一個全新的執行緒,隨後又刪除掉的做法更有效率。

一、執行緒池管理
    執行緒池管埋是指在多執行緒應用程式的初始化過程屮建立執行緒的一個集合,當需要執行緒時,為新任務重用這些執行緒,而不是建立新的執行緒的過程。這個過程中建立的執行緒數量通常部是固定的。然而,增加可用的執行緒數量也是可以的。池中的每個執行緒都被分派一個任務,當任務完成時,執行緒就返回執行緒池中等待下一次分派。

                     

     1.什麼時候需要用執行緒池
    執行緒池在多執行緒應用程式中是必需的,原因如下:
     (1)執行緒池有效改善了應用程式的響應時間,因為執行緒池中的執行緒是現成的,就處於等待任務分派的狀態中,系統無需再從頭建立執行緒。
     (2)線上程池方式下,CLR節省了為每個生存期短暫的任務建立一個全新執行緒,並在其結束時回收其資源的開銷。
     (3)執行緒池根據系統當前正在執行的程序情況優化執行緒時間片。
    (4)執行緒池允許我們在不逐一設定執行緒的屬性的情況下,啟動多個執行緒。
    (5)執行緒池允許我們將狀態資訊作為一個物件傳遞給當前正在執行任務的過程引數。
    (6)執行緒池可以將處理客戶請求的執行緒數量固定為某一個最大值。

    2.執行緒池的概念
    影響多執行緒應用程式的響應性的一個主要原因就是為每個任務建立執行緒的時間開銷。

    例如,Web伺服器是響應10個客戶的訪問,以前的做法是:若伺服器採用為每個客戶建立一個執行緒的策略,則需建立10個新執行緒。伺服器將承擔執行緒的建立、在整個生存期內管理這些執行緒的開銷。在某個時刻,執行緒可能會耗盡整個系統的資源。替代的方法:若伺服器使用執行緒池來響應客戶請求,每當客戶發出請求時,系統就無需再為建立執行緒耗費時間。這就是執行緒池管理方式的重要概念。

    Windows作業系統為響應客戶請求維護一個執行緒池。當我們的應用程式請求一個新的執行緒時,Windows就試圖從池中取出一個,如果執行緒池是空的,那就建立一個新的供我們使用。Windows將動態處理執行緒池的大小以加快應用程式的響應時間。

    影響多執行緒應用程式執行緒設計的因素有:應用程式的響應性、執行緒管理資源的分配、資源共享、執行緒同步。

二、CLR與執行緒池
    CLR是專門用於建立託管程式碼環境,為在.NET平臺上執行的應用程式提供各種服務的,例如編譯、垃圾收集、記憶體管理,還有執行緒池。
     

    確實,在定義宿主應用程式使用的執行緒的程序方面,Win32和.NET Framework有著顯著的差別。

    在傳統的多執行緒Win32應用程式中,每個程序都是由執行緒集合組成的。每個執行緒又由執行緒本地儲存(Thread Local Storage,TLS)、呼叫堆疊組成,用於在單處理器系統中提供時間片。單處理器系統根據執行緒的優先順序為每個執行緒分配時間片。當某個特定執行緒的時間片消耗完時,它就會處於掛起狀態,其他執行緒就將開始執行其任務。
在.NET Framework中,每個程序都可分成多個應用程式域,它是用於宿主執行緒以及TLS和呼叫堆疊的。值得關注的是,程序間的通訊是通過.NETFramework中的一個稱為遠端處理的技術來進行處理的。

    1.CLR管理執行緒池
     CLR構成了.NET Framework的靈魂和核心,為託管應用程式提供多個服務(執行緒池管理就是其中之一)。對於執行緒池中排在佇列中的每個任務(任務項),CLR從執行緒池中指派一個執行緒(工作者執行緒),然後在任務結束時將執行緒釋放回池中。
       

       執行緒池總是通過CLR使用多執行緒單元模式,藉助搶先式多工管理使用高效能的佇列和排程程式來實現的。它是CPU時間被分成多個時間片的一個過程。在每個時間片中,都有個特定的執行緒在執行,而其他執行緒則處於等待狀態。一旦這個時間片用完之後,系統就根據剩餘執行緒的最高優先順序決定由哪個執行緒使用CPU。客戶請求排在任務佇列中,佇列中的毎個任務都將被分配給執行緒池中第一個可用的執行緒。

      一旦執行緒完成了分配給它的任務,它就返回到執行緒池中等待CLR的下一次分配。

     執行緒池的大小可以是固定不變的,也可以是動態變化的。在前面的示例中,執行緒的數量線上程池的生存期間不發生變化。通常情況下,這種型別的執行緒池用於我們確切知道應用程式可用資源的數量的情況,這樣固定數目的執行緒就可以線上程池初始化過程中建立完成。

     面這種情況正好適用於這種型別:我們為企業內部網開發解決方案或者在可以嚴格定義目標平臺的系統需求的應用程式中,大小動態可變的執行緒池適用於不知道可用資源數量的情況,因為在Web伺服器的情況下,我們不知道將要同時處理多少客戶請求。

     2. 避免使用執行緒池的情況
    儘管執行緒池在我們構建多執行緒應用程式時給我們帶來了大量的好處,但是,下面情況應避免使用執行緒池:
     (1)CLR將執行緒池中的執行緒分配給任務,並在任務完成時將執行緒釋放間池中。如果任務已經被新增到佇列中,此時就沒有直接的方法可以終止任務。
     (2)執行緒池管理對於任務生存期較短的情況非常有效,例如Web伺服器響應客戶對某個特定檔案的請求。執行緒池不適用又大又長的任務。
     (3)執行緒池管理是一種以成本效率方式使用執行緒的技術,此處成本效率根據數量和啟動開銷來確定,決定使用池中執行緒的時候要十分小心。執行緒池的大小應該固定不變。
     (4)執行緒池中的所有執行緒都是處於多執行緒單元之中。如果我們想把執行緒放置到單執行緒單元中,那麼執行緒池就沒有用了。
     (5)如果我們想標識執行緒並執行各種操作,例如啟動執行緒、掛起和中止等,那麼執行緒池不能完成這樣的工作。
     (6)同樣,我們不可能對使用執行緒池的任務設定優先順序。
     (7)對任意給定的程序,只能有一個執行緒池與其相關聯。
     (8)如果分配給執行緒池中的一個執行緒的任務被鎖定,那麼這個執行緒將不會再釋放回池中。這種情況可以通過使用有效的程式設計技巧加以避免。

     3.執行緒池的大小
      執行緒池中可以排隊等待的任務數量取決於機器的記憶體數量。同樣,在程序中可以啟用的執行緒數量取決於機器中的CPU個數。

     正如我們己經知道的,這是因為每個處理器在同一時間只能執行一個執行緒,預設情況下,處在多執行緒單元中的執行緒池的每個執行緒都將使用預設的任務,執行庫將採用預設的優先順序。此處使用的單詞“預設”顯得似乎有些不太明確,但這不會產生任何問題。每個系統都有的預設優先順序設定。

     在任意時刻,如果某個執行緒處於空閒狀態,那麼執行緒池就會引導工作者執行緒使所有的處理器保持繁忙。如果執行緒池中的所有執行緒都處於繁忙狀態,並且佇列中有未處理的任務,那麼執行緒池將產生一個新的執行緒來完成待處理的工作。但是,產生的執行緒數量不能超過指定的最大值。
          


      預設情況下,每個程序可以產生25個執行緒池執行緒。然而,這個數量可以通過編輯mscoree.h檔案中定義的CorSetMaxThreads成員加以改變,萬一要是有額外的執行緒請求的話,那麼這個請求將加入到佇列中,直到某些執行緒完成了分配給它的任務返回到執行緒池中為止。

     .NET Framework對非同步呼叫、建立套接字連線和註冊過的等待操作等使用執行緒池功能。

三、ThreadPool 類
    為了在應用程式中使用執行緒池,.NET Framework在System.Threading名稱空間中提供了 ThreadPool類。ThreadPool類提供的執行緒池可以用來解決以下問題: 處理任務項、處埋非同步I/O呼叫、處理計時器、代表其他執行緒等待。
     

    BindHandle(SafeHandle)
       將作業系統控制代碼繫結到 ThreadPool。返回值True繫結成功,False繫結失敗。
       osHandle           儲存作業系統控制代碼的 SafeHandle。在非託管端必須為重疊 I/O 開啟該控制代碼。

     GetAvailableThreads(workerThreads, completionPortThreads) 

       檢索由 GetMaxThreads 方法返回的最大執行緒池執行緒數和當前活動執行緒數之間的差值。

          WorkerThreads          執行緒池中工作者執行緒的最大數目。
         completionPortThreads  執行緒池中非同步 I/O 執行緒的最大數目。

      GetMaxThreads(workerThreads, completionPortThreads)
          檢索可以同時處於活動狀態的執行緒池請求的數目。所有大於此數目的請求將保持排隊狀態,直到執行緒池執行緒變為可用。
          workerThreads           執行緒池中工作者執行緒的最大數目。
          completionPortThreads   執行緒池中非同步 I/O 執行緒的最大數目。

     QueueUserWorkItem(WaitCallback)
      ThreadPool.QueueUserWorkItem(WaitCallback, state)

            將一個任務項排列到執行緒池中。返回值True執行成功,False執行失敗。
           callBack                一個 WaitCallback(委託),表示要執行的方法。
            State                   傳遞給委託的物件。

     RegisterWaitForSingleObject(WaitHandle, WaitOrTimerCallback,state, Int32, Boolean)
        註冊一個等待 WaitHandle 的委託,並指定超時值(毫秒)。
         waitObject                    要註冊的WaitHandle。使用WaitHandle而非Mutex。
          callBack                   向waitObject引數發出訊號時呼叫的WaitOrTimerCallback委託。
         State                       傳遞給委託的物件。
          millisecondsTimeOutInterval 以毫秒為單位的超時。為0立即返回。為-1永遠不過期。
         executeOnlyOnce            為true表示委託後執行緒將不再在waitObject引數上等待;為false表示每次完成等待操作後都重置計時器,直到登出等待。

       UnsafeRegisterWaitForSingleObject(WaitHandle,WaitOrTimerCallback,Object,Int32,Boolean)
           註冊一個等待 WaitHandle 的委託,並使用超時時間(毫秒)。此方法不將呼叫堆疊傳播到輔助執行緒。
          waitObject                     要註冊的 WaitHandle。使用 WaitHandle 而非 Mutex。
          callBack                       向 waitObject 引數發出訊號時呼叫的委託。
           State                          傳遞給委託的物件。
           millisecondsTimeOutInterval    以毫秒為單位的超時。為0立即返回。為-1永遠不過期。
          executeOnlyOnce             為true表示委託後執行緒將不再在waitObject引數上等待;為false表示每次完成等待操作後都重置計時器,直到登出等待。

四、VB.NET中執行緒池的程式設計
        ThreadPool類的3個規則:
        (1)每個ThreadPool物件只能有一個工作者執行緒;
        (2)每個程序只能有一個ThreadPool物件;
        (3)第一次建立ThreadPool物件是當我們呼叫ThreadPool.QueueUserWorkItem( )方法,或者是通過計時器或已註冊等待的操作呼叫回撥方法時發生的。

        ThreadPool類的一個普通用法就是不用設定每個執行緒的屬性而啟動多個獨立的任務。

        例:微軟例子

Imports System.Threading
Public Class Example
    <MTAThread> Public Shared Sub Main()
        ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ThreadProc)) '委託方法入隊
        'ThreadPool.QueueUserWorkItem(AddressOf ThreadProc)              ‘等同上面語句

        Console.WriteLine("Main thread does some wor, then sleeps.")
        Thread.Sleep(1000)

        Console.WriteLine("Main thread exits.")
        Console.ReadLine()
    End Sub
    Shared Sub ThreadProc(stateInfo As Object) '無狀態傳來,為空
        Console.WriteLine("Hello from the thread pool.")
    End Sub
End Class
       說明:建立執行緒池後,兩執行緒入隊,Main執行緒活動時,ThreadProc只能等待;Main執行緒Sleep時,ThreadProc啟用,其執行完後,Main執行緒又啟用。
              

        例:理解執行緒池中各執行緒執行次序

Imports System.Threading
Friend Class ObjState
    Friend inarg1 As String
    Friend inarg2 As String
    Friend outval As String
End Class
Module ThreadAppModule
    Sub Taskl(ByVal StateObj As Object)     '任務1
        Dim StObj As ObjState = CType(StateObj, ObjState) '傳來的狀態轉型別
        Console.WriteLine("Input Argument 1 in task 1:" & StObj.inarg1)
        Console.WriteLine("Input Argument 2 in task 1: " & StObj.inarg2)
        StObj.outval = "From Task1 " & StObj.inarg1 & " " & StObj.inarg2 '此物件還可作返回值
    End Sub
    Sub Task2(ByVal StateObj As Object)     '任務2
        Dim StObj As ObjState = CType(StateObj, ObjState)
        Console.WriteLine("Input Argument 1 in task 2:" & StObj.inarg1)
        Console.WriteLine("Input Argument 2 in task 2:" & StObj.inarg2)
        StObj.outval = "From Task2 " & StObj.inarg1 & " " & StObj.inarg2
    End Sub
    Sub Main()
        Dim StObj1 As New ObjState()
        Dim StObj2 As New ObjState()
        StObj1.inarg1 = "String Param1 of task 1" '分別設定兩物件值
        StObj1.inarg2 = "String Param2 of task 1"
        StObj2.inarg1 = "String Param1 of task 2"
        StObj2.inarg2 = "String Param2 of task 2"
        '程序中只有一個Threadpool,且成員多為共享,故不必單獨為其例項化物件,直接用類。
        ThreadPool.QueueUserWorkItem(New Threading.WaitCallback(AddressOf Taskl), StObj1)
        ThreadPool.QueueUserWorkItem(New Threading.WaitCallback(AddressOf Task2), StObj2)
        Console.Read()
    End Sub
End Module
        說明:任務1與任務2通過執行緒池逐個啟用(反正不讓任務執行緒空閒),傳遞給委託方法的物件都被轉了定義的ObjState型別。
            

       例:理解定時觸發器與開關門的關係

Imports System.Threading
Public Class vbThreadPool
    Private Shared i As Integer = 0
    Public Shared Sub main()
        Dim arev As New AutoResetEvent(False)      '1、自動同步
        'Dim arev As New ManualResetEvent(False)   ‘2、手動同步
        '註冊(新增)一個定時器(定時觸發委託項),第一引數為開關量以確定開或關,此處False為關,受阻。
        ThreadPool.RegisterWaitForSingleObject(arev, AddressOf workitem, Nothing, 1000, False) '3、定時觸發器
        arev.Set()   '4、開關量開啟(True),定時器啟動(如Timer一樣,每1秒呼叫委託方法)
        Console.Read()
    End Sub
    Public Shared Sub workitem(ByVal obj As Object, ByVal signaled As Boolean)
        i += 1
        Console.WriteLine("Thread Pool Work Item Ivoked:" & i.ToString)
    End Sub
End Class
       說明:本例程式碼簡單,理解較難。

       1、理解執行緒池的RegisterWaitForSingleObject。
          它是一個定時觸發器,類似Timer控制元件一樣,即每隔一段時間觸發委託方法。
第一引數WaitHandle是一個關開量(開True,關False),它可以是手動同步ManualResetEvent或自動同步AutoResetEvent,它類似一個大門;第二引數委託方法,類似大門內關著的一匹馬;第三引數state狀態物件,類似給馬的貨物或裝飾(當然也可以沒有Nothing);第四引數間隔時間,類似開大門時需要間隔多久(每開一次門,就跑出一匹馬,方法就執行起來了),第五引數是否只開一次門(方法也就只能執行一次,如右圖)。為真,總共只執行一次;為假,不止執行一次。
          因此,上面3處程式碼可以解釋為:每隔1秒觸發開啟大門,馬兒就跑出來,馬兒無貨物託運,並且總共大門不止開一次。即一直間隔1秒地去開門。

        2、理解Set
          Set的目的,在AutoResentEvent自動同步中,直接開啟門(True),於是下面左圖或右圖第一語句都是沒有等1秒,就執行了。因為Set是另一個人來開啟門(不是定時觸發器來開啟門),也即開啟門有兩種方式。既然門開了,馬兒就直接跑出來了,還管什麼定時觸發器的1秒間隔?所以第一句都沒有等待1秒,就直接顯示出來了。
但後面語句(紅箭頭)都是等待了1秒才顯示。這裡又涉及到手動同步(ManualResetEvent)或自動同步(AutoResetEvent)的區別:
        若是用的1處的自動同步(AutoResetEvent),它就類似自動門一樣,開啟(Set為真)後,跑出一匹馬,然後大門會自動關上(為假),所以這裡定時觸發器的間隔時間就體現出來了:每隔一秒去開大門,跑出馬後會自動關上大門,所以後面每隔一秒會顯示資訊。
         若是用的2處的手動同步(ManualResetEvent),情況就不一樣的,大門不是自動大門,開啟(Set其為True)後不會自動關門(False)。如果用了4處的開大門,大門不會再關閉,大大地敞開,於是馬兒一匹接一匹的向外跑,於是資訊就無間隔的連續一直顯示下去。定時觸發器中的時間間隔就不會起作用。
         本來定時觸發器也有自動關門的作用,因為沒有開也就沒有與之對應的關(因為開是別人做的),所以它也只有乾瞪眼看著一匹接一匹的馬兒跑。如果我們註釋掉1處使用2處語句,同時註釋掉4處,就會發現:第一句也會間隔1秒(因為沒人去開門,所以定時觸發器來開門),然後,後面也是每隔一秒顯示(因為定時觸發器自已開門,也會自動去關門)。
                

五、在.NET中的可伸縮性
      Windows 作業系統管理著如何將執行緒分配給處理器,同時,觸發任何程序會自動啟動一個執行緒。.NET Framework對處理器分配不提供細粒度的控制,它寧願讓作業系統控制排程,因為與CLR相比,處理器提供更多的載入資訊。然而,它還是對整個程序運行於哪個處理器提供了一些控制措施。但是這適用於程序中的所有執行緒,選擇哪些處理器來處理超出了我們的話題。

     如果只有一個執行緒(即主執行緒),那麼執行緒中的每個任務都將運打在相同的處理器上。然而,如果建立一個新的執行緒,那麼作業系統將安排執行緒將在哪個處理器上執行。系統在作出這個判斷時會消耗一些處理器資源,所以對於那些小的任務而言,這種消耗通常不值得,因為執行這些任務的時間幾乎和判斷由哪個處理器來執行執行緒的時間一樣。

     然而,隨著Windows版本的延續,這種分配佔用的時間越來越少,同時對於除最細微的任務之外的事情而言,當使用執行緒時,您將發現通過建立新執行緒來執行任務將提高系統性能。這也只有在對稱多處現器(symmetric multi-processor, SMP)系統中您才能看到執行緒的真正優點,因為所有的處理器都會被充分用於分配承擔應用程式的負荷。
      

      通用執行緒池管理器類的設計
      以前建立執行緒的方法過於鬆散自由,現線上程池又過於僵硬呆板。下面建立一個執行緒池管理類,它維護指定數量執行緒的執行緒池,這些執行緒用於請求的應用程式。這樣既可以在程式碼中更加容易控制這些執行緒,同時在例項化執行緒物件時更快地執行執行緒。簡單地說,就是綜合兩者的優點。

      程式碼較長,邏輯圖有點類似下面(引用別人的圖),只是少了一部分超時的判斷。
                  

        提示:右擊關鍵詞,可以選擇“轉到定義”(迅速到定義處)或“轉到實現”(迅速到實現處),工具欄左端面有一個“後退”,可迅速回到前一個程式碼定位處。


        首先新增一個新的類(管理器類):

Imports System.Threading
Imports System.Text
Namespace GenThreadPool '通用執行緒池管理
    Public Interface IThreadPool       'ThreadPool介面,用於GenThreadPoolImpl類
        Sub AddJob(jobToRun As Thread) '新增作業
        Function GetStats() As Stats   '獲取狀態
    End Interface

    Public Class GenThreadPoolImpl
        Implements IThreadPool
        Private m_maxThreads As Integer         '最多執行緒
        Private m_minThreads As Integer         '最少執行緒
        Private m_maxIdleTime As Integer        '最長空閒時間(超時刪除對應執行緒)
        Private Shared m_debug As Boolean       '是否除錯
        Private m_pendingJobs As ArrayList      '等待的作業量(陣列形式,以便列隊進入執行緒池)
        Private m_availableThreads As ArrayList '可用執行緒數

        Public Property PendingJobs() As ArrayList
            Get
                Return m_pendingJobs
            End Get
            Set
                m_pendingJobs = Value
            End Set
        End Property
        Public Property AvailableThreads() As ArrayList
            Get
                Return m_availableThreads
            End Get
            Set
                m_availableThreads = Value
            End Set
        End Property
        Public Property Debug() As Boolean
            Get
                Return m_debug
            End Get
            Set
                m_debug = Value
            End Set
        End Property
        Public Property MaxIdleTime() As Integer
            Get
                Return m_maxIdleTime
            End Get
            Set
                m_maxIdleTime = Value
            End Set
        End Property
        Public Property MaxThreads() As Integer
            Get
                Return m_maxThreads
            End Get
            Set
                m_maxThreads = Value
            End Set
        End Property
        Public Property MinThreads() As Integer
            Get
                Return m_minThreads
            End Get
            Set
                m_minThreads = Value
            End Set
        End Property

        Public Sub New()       '預設構造。只允許1個執行緒在池中,0.3秒後銷燬
            m_maxThreads = 1
            m_minThreads = 0
            m_maxIdleTime = 300
            m_pendingJobs = ArrayList.Synchronized(New ArrayList)      '對陣列同步包裝(仍為陣列),因為這樣上
            m_availableThreads = ArrayList.Synchronized(New ArrayList) '鎖才執行緒安全,以防其它執行緒同時進行修改。
            m_debug = False
        End Sub
        Public Sub New(ByVal maxThreads As Integer, ByVal minThreads As Integer, ByVal maxIdleTime As Integer)
            '建構函式,用3個引數例項化
            m_maxThreads = maxThreads
            m_minThreads = minThreads
            m_maxIdleTime = maxIdleTime
            m_pendingJobs = ArrayList.Synchronized(New ArrayList)
            m_availableThreads = ArrayList.Synchronized(New ArrayList)
            m_debug = False
            InitAvailableThreads()
        End Sub
        Private Sub InitAvailableThreads() '初始化執行緒池,分別進入池中
            If m_maxThreads > 0 Then
                For i As Integer = 1 To m_maxThreads
                    Dim t As New Thread(AddressOf (New GenPool(Me, Me)).Run)
                    Dim e As New ThreadElement(t)
                    e.Idle = True
                    m_availableThreads.Add(e)
                Next
            End If
        End Sub
        Public Sub New(ByVal maxThreads As Integer, ByVal minThreads As Integer, ByVal maxIdleTime As Integer, ByVal debug As Boolean)
            '建構函式,用4個引數例項化,增加除錯引數
            m_maxThreads = maxThreads
            m_minThreads = minThreads
            m_maxIdleTime = maxIdleTime
            m_pendingJobs = ArrayList.Synchronized(New ArrayList)
            m_availableThreads = ArrayList.Synchronized(New ArrayList)
            m_debug = debug
            InitAvailableThreads()
        End Sub
        Public Sub AddJob(ByVal job As Thread) Implements IThreadPool.AddJob '向池中新增作業
            If job Is Nothing Then
                Return  '作業不存在,退出
            End If

            SyncLock Me                   '鎖定GenThreadPoolImpl,防止其它執行緒來新增或刪除作業
                m_pendingJobs.Add(job)    '將作業新增到 ArrayList 的結尾處。
                Dim index As Integer = FindFirstIdleThread()     '空閒可用執行緒的索引
                If m_debug Then
                    Console.WriteLine("First Idle Thread Is " & index.ToString)
                End If

                If index = -1 Then        '-1無空閒執行緒,故需建立新的執行緒
                    If m_maxThreads = -1 Or m_availableThreads.Count < m_maxThreads Then
                        '池中無執行緒,或在用(有效)執行緒還未達最大執行緒數限制--->建立執行緒
                        If m_debug Then
                            Console.WriteLine("Creating a New thread")
                        End If

                        Dim t As New Thread(AddressOf (New GenPool(Me, Me)).Run)
                        Dim e As New ThreadElement(t) '幫助類,提供執行緒額外屬性
                        e.Idle = False
                        e.GetMyThread.Start()         '執行緒新增到ArrayList陣列前先激發

                        Try                           '新增
                            m_availableThreads.Add(e)
                        Catch ex As OutOfMemoryException
                            Console.WriteLine("Out Of memory: " & ex.ToString)
                            Thread.Sleep(3000)
                            m_availableThreads.Add(e)
                            Console.WriteLine("Added Job again")
                        End Try
                        Return
                    End If

                    If m_debug Then
                        Console.WriteLine("No Threads Available...” & GetStats.ToString)
                    End If
                Else  '池中找到有空的執行緒
                    Try
                        If m_debug Then
                            Console.WriteLine("Using an existing thread...")
                        End If

                        CType(m_availableThreads(index), ThreadElement).Idle = False   '標註忙碌
                        SyncLock CType(m_availableThreads(index), ThreadElement).GetMyThread()
                            Monitor.Pulse(CType(m_availableThreads(index), ThreadElement).GetMyThread())
                        End SyncLock
                    Catch ex As Exception
                        Console.WriteLine(("Error while reusing thread " & ex.Message))
                        If m_debug Then
                            Console.WriteLine("Value of index Is " & index.ToString)
                            Console.WriteLine("Size of available threads Is " & m_availableThreads.Count.ToString)
                            Console.WriteLine("Available Threads Is " & m_availableThreads.IsSynchronized.ToString)
                        End If
                    End Try
                End If
            End SyncLock
        End Sub
        Public Function GetStats() As Stats Implements IThreadPool.GetStats  '池中狀態
            Dim statsInstance As New Stats()
            statsInstance.MaxThreads = m_maxThreads
            statsInstance.MinThreads = m_minThreads
            statsInstance.MaxIdleTime = m_maxIdleTime                     '最大空閒時間
            statsInstance.PendingJobs = m_pendingJobs.Count               '等待的作業量
            statsInstance.NumThreads = m_availableThreads.Count           '有效執行緒數
            statsInstance.JobsInProgress = m_availableThreads.Count - FindIdleThreadCount() '正在處理的作業量
            Return statsInstance
        End Function
        Public Function FindIdleThreadCount() As Integer  '遍歷有效執行緒,返回空閒執行緒數
            Dim idleThreads As Integer = 0
            For i As Integer = 0 To m_availableThreads.Count - 1
                If CType(m_availableThreads(i), ThreadElement).Idle Then '根據幫助類中的空閒標誌Idle來統計
                    idleThreads += 1
                End If
            Next
            Return idleThreads
        End Function
        Public Function FindFirstIdleThread() As Integer '遍歷,找到第一個空閒執行緒,就立即返回其索引
            For i As Integer = 0 To m_availableThreads.Count - 1
                If CType(m_availableThreads(i), ThreadElement).Idle Then
                    Return i
                End If
            Next
            Return -1
        End Function
        Public Function FindThread() As Integer '查詢當前執行緒的位置(索引號),失敗為-1(說明池中無執行緒)
            For i As Integer = 0 To m_availableThreads.Count - 1
                If CType(m_availableThreads(i), ThreadElement).GetMyThread.Equals(Thread.CurrentThread) Then
                    Return i
                End If
            Next
            Return -1
        End Function
        Public Sub RemoveThread() '移除執行緒
            For i As Integer = 0 To m_availableThreads.Count - 1
                If CType(m_availableThreads(i), ThreadElement).GetMyThread.Equals(Thread.CurrentThread) Then
                    m_availableThreads.RemoveAt(i)
                    Exit Sub
                End If
            Next
        End Sub
    End Class
    Public Class GenPool '執行執行緒類(執行完畢後,過了指定超時時限,將自動從池中刪除)
        Private m_lock As Object           '鎖定物件
        Private m_gn As GenThreadPoolImpl
        Public Sub New(lock_ As Object, gn As GenThreadPoolImpl)
            m_lock = lock_
            m_gn = gn
        End Sub
        Public Sub Run() '迴圈執行並檢測是否過期
            Dim job As Thread
            While True '無限迴圈,一直檢查池中狀態
                While True
                    SyncLock m_lock
                        If m_gn.PendingJobs.Count = 0 Then           '後續無作業進來
                            Dim index As Integer = m_gn.FindThread() '取當前執行緒索引號
                            If index = -1 Then               '無作業,池中也無執行緒,退出
                                Exit Sub
                            End If                                 '無作業,新的執行緒設為空閒
                            CType(m_gn.AvailableThreads(index), ThreadElement).Idle = True
                            Exit While
                        End If
                        job = CType(m_gn.PendingJobs(0), Thread) '有作業,取出(從原作業陣列中刪除)
                        m_gn.PendingJobs.RemoveAt(0)
                    End SyncLock
                    job.Start()             '作業執行啟動
                End While
                Try   '無後續作業
                    SyncLock Me
                        If m_gn.MaxIdleTime = -1 Then '池中無空閒執行緒,阻塞等待
                            Monitor.Wait(Me)
                        Else
                            Monitor.Wait(Me, m_gn.MaxIdleTime)
                        End If
                    End SyncLock
                Catch
                End Try
                SyncLock m_lock
                    If m_gn.PendingJobs.Count = 0 Then '無等待的作業(沒有新的作業進來)
                        If m_gn.MinThreads <> -1 And m_gn.AvailableThreads.Count > m_gn.MinThreads Then
                            m_gn.RemoveThread() '池中執行緒不空,且有效執行緒大於最小執行緒,刪除執行緒
                            Return
                        End If
                    End If
                End SyncLock
            End While
        End Sub
    End Class

    Public Class ThreadElement '對應執行緒的執行緒幫助類(以例為之設計空閒標誌,獲取引用)
        Private m_idle As Boolean   '空閒執行緒標誌
        Private m_thread As Thread
        Public Sub New(th As Thread)
            m_thread = th
            m_idle = True      '初始化即為空閒
        End Sub
        Public Property Idle() As Boolean '設定或獲取執行緒空閒標誌
            Get
                Return m_idle
            End Get
            Set
                m_idle = Value
            End Set
        End Property
        Public Function GetMyThread() As Thread '取得原執行緒
            Return m_thread
        End Function
    End Class

    Public Structure Stats  '狀態統計
        Public MaxThreads As Integer
        Public MinThreads As Integer
        Public MaxIdleTime As Integer
        Public NumThreads As Integer
        Public PendingJobs As Integer     '列隊等待的作業量
        Public JobsInProgress As Integer  '正在處理的作業量
        Public Overrides Function ToString() As String        '提取狀態
            Dim sb As New StringBuilder("MaxThreads = ", 107) '容量大小107字元
            sb.Append(MaxThreads)
            sb.Append(ControlChars.Lf & "MinThreads=" & MinThreads)
            sb.Append(ControlChars.Lf & "MaxIdleTime=" & MaxIdleTime)
            sb.Append(ControlChars.Lf & "PendingJobs=" & PendingJobs)
            sb.Append(ControlChars.Lf & "JobsInProgress=" & JobsInProgress)
            Return sb.ToString
        End Function
    End Structure
End Namespace


           然後,完成主程式程式碼,用來測試:
Imports System.Threading
Namespace TestGenThreadPool
    Public Class TestPerformance
        Public count As Integer
        Private m_lock As New Object()

        Public Sub New(pool As GenThreadPool.IThreadPool, times As Integer)
            Console.WriteLine("Performance using Pool [in ms]: ")
            count = 0
            Dim start As Long = Now.Millisecond
            Console.WriteLine("Start Time For Job Is " & Now)
            Dim i As Integer
            For i = 0 To times - 1
                Dim tl As New Thread(AddressOf (New Job(Me)).Run)
                pool.AddJob(tl)
            Next

            While True
                SyncLock m_lock
                    If count = times Then
                        Exit While
                    End If
                End SyncLock


                Try
                    Thread.Sleep(5000)
                Catch
                End Try
            End While

            Console.WriteLine(" " & (Now.Millisecond - start).ToString)
            Console.WriteLine("End Time for Job is " & Now.ToString)
            Console.WriteLine("Performance using no Pool [in ms]: ")
            count = 0
            start = Now.Millisecond
            Console.WriteLine("Start Time for JobThread is " & Now.ToString)
            For i = 0 To times - 1
                Dim jt As New Thread(AddressOf (New JobThread(Me)).Run)
                jt.Start()
            Next

            While True
                SyncLock m_lock
                    If count = times Then
                        Exit While
                    End If
                End SyncLock
                Try
                    Thread.Sleep(5000)
                Catch
                End Try
            End While
            Console.WriteLine(" " & (Now.Millisecond - start).ToString())
            Console.WriteLine("End Time for JobThread is ” & Now.ToString)
        End Sub
        NotInheritable Class JobThread
            Private m_lock As New Object()
            Private tpf As TestPerformance
            Public Sub New(tpf_ As TestPerformance)
                tpf = tpf_
            End Sub
            Public Sub Run()
                SyncLock m_lock
                    tpf.count += 1
                End SyncLock
            End Sub
        End Class
        NotInheritable Class Job
            Private m_lock As New Object()
            Private tpf As TestPerformance

            Public Sub New(tpf_ As TestPerformance)
                tpf = tpf_
            End Sub
            Public Sub Run()
                SyncLock m_lock
                    tpf.count += 1
                End SyncLock
            End Sub
        End Class
    End Class
    Class TestPool
        Private Shared i As Integer = 0
        Private j As Integer = 0
        Public Sub Run()
            i += 1
            j = i
            Console.WriteLine("Value of i in run is {0} ", j)
        End Sub
        Public Shared Sub Main(args() As String)
            Dim tp = New GenThreadPool.GenThreadPoolImpl(1000, 1000, 300, True)
            Dim i As Integer
            For i = 0 To 99 '新增作業到執行緒池管理器
                Dim td1 As New TestPool
                Dim t1 As New Thread(AddressOf td1.Run)
                Dim td2 As New TestPool
                Dim t2 As New Thread(AddressOf td2.Run)
                Dim td3 As New TestPool
                Dim t3 As New Thread(AddressOf td3.Run)
                Dim td4 As New TestPool
                Dim t4 As New Thread(AddressOf td4.Run)
                Dim td5 As New TestPool
                Dim t5 As New Thread(AddressOf td5.Run)
                Dim td6 As New TestPool
                Dim t6 As New Thread(AddressOf td6.Run)
                Dim td7 As New TestPool
                Dim t7 As New Thread(AddressOf td7.Run)
                Dim td8 As New TestPool
                Dim t8 As New Thread(AddressOf td8.Run)
                Dim td9 As New TestPool
                Dim t9 As New Thread(AddressOf td9.Run)
                Dim td10 As New TestPool
                Dim t10 As New Thread(AddressOf td10.Run)
                Dim td11 As New TestPool
                Dim t11 As New Thread(AddressOf td11.Run)
                tp.AddJob(t1)
                tp.AddJob(t2)
                tp.AddJob(t3)
                tp.AddJob(t4)
                tp.AddJob(t5)
                tp.AddJob(t6)
                tp.AddJob(t7)
                tp.AddJob(t8)
                tp.AddJob(t9)
                tp.AddJob(t10)
                tp.AddJob(t11)
            Next
            Dim td12 As New TestPool
            Dim t12 As New Thread(AddressOf td12.Run)
            tp.AddJob(t12)
            Dim p As New TestPerformance(tp, 1000)
        End Sub
    End Class
End Namespace

      結果較長,同時播放視訊時,就可明顯感覺到視訊有點卡頓,說明CPU有點忙不過來了:)