1. 程式人生 > >對執行緒池的理解

對執行緒池的理解

          自從看了老趙關於執行緒池的實驗以後,我就想學著做一個類似的實驗,驗證自己的理解,現在終於做好了,請大家指正。

          一般情況下我們都使用Thread類建立執行緒,因為通過Thread物件可以對執行緒進行靈活的控制。但建立執行緒和銷燬執行緒代價不菲,過多的執行緒會消耗掉大量的記憶體和CPU資源,假如某段時間內突然爆發了100個短小的執行緒,建立和銷燬這些執行緒就會消耗很多時間,可能比執行緒本身執行的時間還長。為了改善這種狀況,.NET提供了一種稱之為執行緒池(Thread Pool)的技術。執行緒池提供若干個固定執行緒輪流為大量的任務服務,比如用10個執行緒輪流執行100個任務,當一個執行緒完成任務時,並不馬上銷燬,而是接手另一個任務,從而減少建立和銷燬執行緒的消耗。

一、SetMaxThreads()的作用

    MSDN裡這樣解釋:“檢索可以同時處於活動狀態的執行緒池請求的數目。所有大於此數目的請求將保持排隊狀態,直到執行緒池執行緒變為可用。”。這句話可真夠拗口,按我的理解,就是設定執行緒池中所能容納的最大執行緒數目,當任務數大於這個最大值時,多餘的任務將線上程池外排隊等候。

下面我們做個試驗來驗證一下,向一個執行緒池中排入50個執行緒,觀察活動執行緒數(正在執行任務的執行緒,不包括空閒執行緒)的變化情況。

using System;
using System.Collections.Generic;
using System.Text;
using 
System.Threading; using System.Diagnostics; namespace ThreadPoolTest { class Program { public static void ThreadPoolTest() { ThreadPool.SetMaxThreads(20, 20); //執行緒數目上限設定為20 ThreadPool.SetMinThreads(10, 10); //執行緒數目下限設定為10 Console.WriteLine("活動執行緒數\t"
+ "執行緒名稱\t" + "狀態"); //向執行緒池中新增50個工作執行緒 for (int i = 1; i <= 50; i++) { ThreadPool.QueueUserWorkItem(new WaitCallback(WorkFunction), i); } } private static object Lock = new object(); //工作函式 public static void WorkFunction(object n) { lock (Lock) { Console.WriteLine(GetNumberOfRuningThreads() + "\t\tTask" + n + "\t\tstarted"); } //Do something Thread.Sleep(18681); lock (Lock) { Console.WriteLine(GetNumberOfRuningThreads() + "\t\tTask" + n + "\t\tfinished"); } } //獲取正在執行任務的執行緒數 public static int GetNumberOfRuningThreads() { int max1, max2; ThreadPool.GetMaxThreads(out max1, out max2); int available1, available2; ThreadPool.GetAvailableThreads(out available1, out available2); return (max1 - available1); } static void Main(string[] args) { ThreadPoolTest(); Console.ReadKey(); //按下任意鍵結束程式 } } }

執行結果如下:

活動執行緒數      執行緒名稱        狀態
1                Task1           started
2                Task2           started
3                Task3           started
4                Task4           started
5                Task5           started
6                Task6           started
7                Task7           started
8                Task8           started
9                Task9           started
10              Task10          started
11              Task11          started
12              Task12          started
13              Task13          started
14              Task14          started
15              Task15          started
16              Task16          started
17              Task17          started
18              Task18          started
19              Task19          started
20              Task20          started
20              Task1           finished
20              Task21          started
20              Task3           finished
20              Task22          started
20              Task2           finished
20              Task23          started
20              Task4           finished
20              Task24          started
20              Task6           finished
20              Task25          started
20              Task8           finished
20              Task26          started
20              Task5           finished
20              Task27          started
20              Task9           finished
20              Task28          started
20              Task7           finished
20              Task29          started
20              Task10          finished
20              Task30          started
20              Task11          finished
20              Task31          started
20              Task12          finished
20              Task32          started
20              Task13          finished
20              Task33          started
20              Task14          finished
20              Task34          started
20              Task15          finished
20              Task35          started
20              Task16          finished
20              Task36          started
20              Task17          finished
20              Task37          started
20              Task18          finished
20              Task38          started
20              Task19          finished
20              Task39          started
20              Task20          finished
20              Task40          started
20              Task21          finished
20              Task41          started
20              Task22          finished
20              Task42          started
20              Task23          finished
20              Task43          started
20              Task24          finished
20              Task44          started
20              Task25          finished
20              Task45          started
20              Task26          finished
20              Task46          started
20              Task28          finished
20              Task47          started
20              Task29          finished
20              Task48          started
20              Task27          finished
20              Task49          started
20              Task30          finished
20              Task50          started
20              Task31          finished
19              Task32          finished
18              Task33          finished
17              Task34          finished
16              Task35          finished
15              Task36          finished
14              Task37          finished
13              Task38          finished
12              Task39          finished
11              Task40          finished
10              Task41          finished
9               Task42          finished
8               Task43          finished
7               Task44          finished
6               Task45          finished
5               Task46          finished
4               Task47          finished
3               Task48          finished
2               Task49          finished
1               Task50          finished

  (沒想到資料這麼完美,運氣挺好的)

從上面的結果可以看出,執行緒的隨著任務的增加,活動執行緒的數目也逐步增加,直到達到上限值20為止,之後,活動執行緒的數目保持不變,多餘的任務線上程池外排隊,當有執行緒完成任務時,就從排隊等候的執行緒中取一個來執行,因此雖然任務數在變化,但執行緒數目保持不變。隨著任務的不斷完成,某一時刻,任務數少於20個,此時活動執行緒也開始逐步減少。

二、SetMinThreads()的作用

       MSDN中這樣解釋:“檢索執行緒池在新請求預測中維護的空閒執行緒數。”

       這句話我也挺“專業”的,不太好懂。我的理解是:設定執行緒池中執行緒數目的下限,當任務小於下限時,不足的用空執行緒補足。

如何才能驗證這一點呢?我首先想到的是察看匯流排程數目的變化情況(包括執行任務的和空閒的執行緒),如果匯流排程的最小值是MinThreads,就說明我們的猜測是正確的。但我找了半天,ThreadPool類並沒有提供這樣的介面,所以我們需要另想辦法。

     我的辦法是觀察執行緒的建立的時間。按照設想,執行緒池中本來就有若干空執行緒,所以一開始執行緒開始的時間非常快。當空執行緒用完時,也就是當任務數量會超出執行緒數量時,執行緒池並不會立即建立新執行緒,而是等待大約500毫秒左右,這麼做的目的是看看在這段時間內是否有其他執行緒完成任務來接手這個請求,這樣就可以避免因建立新執行緒而造成的消耗。所以,後面的執行緒建立速度應該比較慢。

     總之一開始,建立執行緒的速度應該是很快的,當執行緒數超過下限值時,建立速度就會慢下來。

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace ThreadPoolTest
{
    class Program
    {
        public static Stopwatch watch = new Stopwatch();

        public static void ThreadPoolTest()
        {
            watch.Start();

            ThreadPool.SetMaxThreads(20, 20);    //執行緒數目上限設定為20
            ThreadPool.SetMinThreads(10, 10);    //執行緒數目下限設定為10

            Console.WriteLine("時間\t\t活動執行緒數\t" + "執行緒名稱\t" + "狀態");

            //向執行緒池中新增50個工作執行緒
              for (int i = 1; i <= 50; i++)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(WorkFunction), i);
            }
        }

        private static object Lock = new object();
        //工作函式
         public static void WorkFunction(object n)
        {
            lock (Lock)
            {
                Console.WriteLine(watch.Elapsed + "\t" + GetNumberOfRuningThreads() + "\t\tTask" + n + "\t\tstarted");
            }

            //Do something
            Thread.Sleep(38681);
             
            lock (Lock)
            {
                Console.WriteLine(watch.Elapsed + "\t" + GetNumberOfRuningThreads() + "\t\tTask" + n + "\t\tfinished");
            }

        }

        //獲取正在執行任務的執行緒數
         public static int GetNumberOfRuningThreads()
        {
            int max1, max2;
            ThreadPool.GetMaxThreads(out max1, out max2);
            int available1, available2;
            ThreadPool.GetAvailableThreads(out available1, out available2);

            return (max1 - available1);
        }

        static void Main(string[] args)
        {
            ThreadPoolTest();            
                 
            Console.ReadKey();  //按下任意鍵結束程式       
        }
    }
}

執行結果如下:

時間                    活動執行緒數      執行緒名稱        狀態
00:00:00.0107238        1                Task1            started
00:00:00.0140175        3                Task3            started
00:00:00.0151379        7                Task7            started
00:00:00.0160629        8                Task2            started
00:00:00.0165277        8                Task8            started
00:00:00.0167671        9                Task5            started
00:00:00.0169506        10              Task9            started
00:00:00.0170967        10              Task4            started
00:00:00.0172783        10              Task10          started
00:00:00.0174646        10              Task6            started
00:00:01.0162788        11              Task11          started
00:00:02.0156464        12              Task12          started
00:00:03.0152283        13              Task13          started
00:00:04.0146323        14              Task14          started
00:00:05.0141607        15              Task15          started
00:00:06.0136234        16              Task16          started
00:00:07.0640600        17              Task17          started
00:00:08.0624471        18              Task18          started
00:00:09.0621401        19              Task19          started
00:00:10.0619472        20              Task20          started
00:00:38.7566261        20              Task1           finished
00:00:38.7576175        20              Task3           finished
00:00:38.7586037        20              Task22          started
00:00:38.7589606        20              Task10          finished
00:00:38.7609881        20              Task23          started
00:00:38.7644790        20              Task4           finished
00:00:38.7653911        20              Task24          started
00:00:38.7661620        20              Task9           finished
00:00:38.7667461        20              Task25          started
00:00:38.7676772        20              Task21          started
00:00:38.7683277        20              Task8           finished
00:00:38.7689285        20              Task26          started
00:00:38.7693858        20              Task2           finished
00:00:38.7700532        20              Task6           finished
00:00:38.7706129        20              Task28          started
00:00:38.7711820        20              Task5           finished
00:00:38.7716580        20              Task29          started
00:00:38.7722943        20              Task27          started
00:00:38.7728069        20              Task7           finished
00:00:38.7733115        20              Task30          started
00:00:39.7583735        20              Task11          finished
00:00:39.7591793        20              Task31          started
00:00:40.7578469        20              Task12          finished
00:00:40.7593474        20              Task32          started
00:00:41.7574471        20              Task13          finished
00:00:41.7582969        20              Task33          started
00:00:42.7569026        20              Task14          finished
00:00:42.7576400        20              Task34          started
00:00:43.7563349        20              Task15          finished
00:00:43.7570931        20              Task35          started
00:00:44.7559455        20              Task16          finished
00:00:44.7567992        20              Task36          started
00:00:45.7593267        20              Task17          finished
00:00:45.7599964        20              Task37          started
00:00:46.7568787        20              Task18          finished
00:00:46.7575400        20              Task38          started
00:00:47.7562341        20              Task19          finished
00:00:47.7569503        20              Task39          started
00:00:48.7568063        20              Task20          finished
00:00:48.7574898        20              Task40          started
00:01:17.4420354        20              Task22          finished
00:01:17.4438299        20              Task41          started
00:01:17.4464655        20              Task23          finished
00:01:17.4478110        20              Task42          started
00:01:17.4488933        20              Task24          finished
00:01:17.4496002        20              Task43          started
00:01:17.4508097        20              Task21          finished
00:01:17.4513800        20              Task44          started
00:01:17.4520468        20              Task25          finished
00:01:17.4526202        20              Task45          started
00:01:17.4532567        20              Task28          finished
00:01:17.4537159        20              Task46          started
00:01:17.4546202        20              Task26          finished
00:01:17.4552586        20              Task47          started
00:01:17.4558928        20              Task27          finished
00:01:17.4564289        20              Task48          started
00:01:17.4571296        20              Task30          finished
00:01:17.4577266        20              Task49          started
00:01:17.4582889        20              Task29          finished
00:01:17.4592571        20              Task50          started
00:01:18.4418865        20              Task31          finished
00:01:19.4424421        19              Task32          finished
00:01:20.4399553        18              Task33          finished
00:01:21.4405072        17              Task34          finished
00:01:22.4400143        16              Task35          finished
00:01:23.4394151        15              Task36          finished
00:01:24.4429568        14              Task37          finished
00:01:25.4404414        13              Task38          finished
00:01:26.4399282        12              Task39          finished
00:01:27.4404093        11              Task40          finished
00:01:56.1459537        10              Task41          finished
00:01:56.1485649        9               Task42          finished
00:01:56.1505286        8               Task43          finished
00:01:56.1514803        7               Task44          finished
00:01:56.1534702        6               Task45          finished
00:01:56.1544100        5               Task46          finished
00:01:56.1552989        4               Task47          finished
00:01:56.1574599        3               Task48          finished
00:01:56.1586138        2               Task49          finished
00:01:56.1602353        1               Task50          finished

(注意,由於執行緒輪換的不確定性,每次結果都會稍有不同)

從結果可以看出,前十個執行緒很短時間內開始,從第十一個執行緒開始則要經過較長一段時間才會建立(大約一秒)。從第二十個執行緒開始,要等前面的任務完成,新任務才能開始(這裡等了一段較長時間),完成一個,就開始一個,執行緒總數保持不變。直到任務數小於執行緒上限,活動執行緒數才開始逐漸減少。

總之,執行緒的執行過程可以描述如下(為了敘述方便,我們假設下限為10,上限為20):

1.當執行緒池被建立後,裡面就會建立10個空執行緒(和下限值相同)。

2.當我們向執行緒池中排入一個任務後,就會有一個空執行緒接手該任務,然後執行起來。隨著我們不斷向執行緒池中排入任務,執行緒池中的空執行緒逐一接手並執行任務。

3.隨著任務的不斷增加,在某一時刻任務數量會超出下限,這時執行緒的數量就不夠用了,但執行緒池並不會立即建立新執行緒,而是等待大約500毫秒左右,這麼做的目的是看看在這段時間內是否有其他執行緒完成任務來接手這個請求,這樣就可以避免因建立新執行緒而造成的消耗。如果這段時間內沒有執行緒完成任務,就建立一個新執行緒去執行新任務。

4.在任務數量超過下限後,隨著新任務的不斷排入,執行緒池中執行緒數量持續增加,直至執行緒數量達到上限值為止。

5.當執行緒數量達到上限時,繼續增加任務,執行緒數量將不再增加。比如你向執行緒池中排入50個任務,則只有20個進入執行緒池(和上限相同),另外30個線上程池外排隊等待。當執行緒池中的某個執行緒完成任務後,並不會立即終止,而是從等待佇列中選擇一個任務繼續執行,這樣就減少了因建立和銷燬執行緒而消耗的時間。

6.隨著任務逐步完成,執行緒池外部等候的任務被逐步調入執行緒池,任務的數量逐步減少,但執行緒的數量保持恆定,始終為20(和上限值相同)。

7.隨著任務被逐步完成,總有某一時刻,任務數量會小於上限值,這時執行緒池內多餘的執行緒會在空閒2分鐘後被釋放並回收相關資源。執行緒數目逐步減少,直到達到下限值為止。

8.當任務數量減小到下限值之下時,執行緒池中的執行緒數目保持不變(始終和下限值相同),其中一部分在執行任務,另一部分處於空執行狀態。

9.當所有任務都完成後,執行緒池恢復初始狀態,執行10個空執行緒。

由上面的論述可以看出執行緒池提高效率的關鍵是一個執行緒完成任務後可以繼續為其他任務服務,這樣就可以使用有限的幾個固定執行緒輪流為大量的任務服務,從而減少了因頻繁建立和銷燬執行緒所造成的消耗。

下面是修改Sleep()時間後再次執行的結果。

時間                    活動執行緒數      執行緒名稱        狀態
00:00:00.0189359        9                Task2           started
00:00:00.0201952        9                Task1           started
00:00:00.0208337        9                Task9           started
00:00:00.0212512        9                Task6           started
00:00:00.0217229        9                Task8           started
00:00:00.0223773        9                Task7           started
00:00:00.0227899        9                Task4           started
00:00:00.0231066        9                Task3           started
00:00:00.0235849        9                Task5           started
00:00:01.0163201        10              Task10          started
00:00:02.0157285        11              Task11          started
00:00:03.0153552        12              Task12          started
00:00:04.0147554        13              Task13          started
00:00:05.0142889        14              Task14          started
00:00:06.0208071        15              Task15          started
00:00:06.7049884        15              Task2           finished
00:00:06.7070401        15              Task16          started
00:00:06.7074667        15              Task7           finished
00:00:06.7078560        15              Task17          started
00:00:06.7082879        15              Task3           finished
00:00:06.7086411        15              Task18          started
00:00:06.7092260        15              Task5           finished
00:00:06.7096711        15              Task19          started
00:00:06.7101447        15              Task6           finished
00:00:06.7111514        15              Task20          started
00:00:06.7146020        15              Task8           finished
00:00:06.7154948        15              Task21          started
00:00:06.7176304        15              Task9           finished
00:00:06.7193209        15              Task22          started
00:00:06.7201892        15              Task1           finished
00:00:06.7208777        15              Task23          started
00:00:06.7216695        15              Task4           finished
00:00:06.7230875        15              Task24          started
00:00:07.5200857        16              Task25          started
00:00:07.7023973        16              Task10          finished
00:00:07.7036477        16              Task26          started
00:00:08.5195702        17              Task27          started
00:00:08.7011903        17              Task11          finished
00:00:08.7025396        17              Task28          started
00:00:09.5187647        18              Task29          started
00:00:09.7008700        18              Task12          finished
00:00:09.7019751        18              Task30          started
00:00:10.5246393        19              Task31          started
00:00:10.7015599        19              Task13          finished
00:00:10.7025033        19              Task32          started
00:00:11.5184213        20              Task33          started
00:00:11.7000063        20              Task14          finished
00:00:11.7006650        20              Task34          started
00:00:12.6995112        20              Task15          finished
00:00:12.7003309        20              Task35          started
00:00:13.3848916        20              Task16          finished
00:00:13.3854166        20              Task36          started
00:00:13.3861911        20              Task17          finished
00:00:13.3865749        20              Task37          started
00:00:13.3875311        20              Task18          finished
00:00:13.3881098        20              Task38          started
00:00:13.3887310        20              Task19          finished
00:00:13.3893513        20              Task39          started
00:00:13.3920574        20              Task20          finished
00:00:13.3925907        20              Task40          started
00:00:13.3949921        20              Task21          finished
00:00:13.3956187        20              Task41          started
00:00:13.3980055        20              Task22          finished
00:00:13.3985151        20              Task42          started
00:00:13.3998460        20              Task23          finished
00:00:13.4002990        20              Task43          started
00:00:13.4022154        20              Task24          finished
00:00:13.4027421        20              Task44          started
00:00:14.1998829        20              Task25          finished
00:00:14.2003381        20              Task45          started
00:00:14.3827441        20              Task26          finished
00:00:14.3833120        20              Task46          started
00:00:15.1987473        20              Task27          finished
00:00:15.1993380        20              Task47          started
00:00:15.3824547        20              Task28          finished
00:00:15.3830691        20              Task48          started
00:00:16.1973072        20              Task29          finished
00:00:16.1980986        20              Task49          started
00:00:16.3807865        20              Task30          finished
00:00:16.3814246        20              Task50          started
00:00:17.2490713        20              Task31          finished
00:00:17.4271196        19              Task32          finished
00:00:18.2423627        18              Task33          finished
00:00:18.4271299        17              Task34          finished
00:00:19.4241792        16              Task35          finished
00:00:20.1084177        15              Task36          finished
00:00:20.1103091        14              Task37          finished
00:00:20.1113160        13              Task38          finished
00:00:20.1123464        12              Task39          finished
00:00:20.1162134        11              Task40          finished
00:00:20.1190785        10              Task41          finished
00:00:20.1220864        9               Task42          finished
00:00:20.1229967        8               Task43          finished
00:00:20.1259507        7               Task44          finished
00:00:20.9331819        6               Task45          finished
00:00:21.1069320        5               Task46          finished
00:00:21.9229240        4               Task47          finished
00:00:22.1070568        3               Task48          finished
00:00:22.9214465        2               Task49          finished
00:00:23.1049741        1               Task50          finished