多線程之旅(11)_如何限制系統線程池ThreadPool的最大最小並發數量_SetMaxThreads/SetMinThreads用法


轉自https://blog.csdn.net/smooth_tailor/article/details/52460566

ThreadPool有兩個設置線程池並發數量的方法,分別是:

ThreadPool.SetMinThreads(int workerThreads, int completionPortThreads)//設置最小線程並發數

ThreadPool.SetMaxThreads(int workerThreads, int completionPortThreads)//設置最大線程並發數

參數解釋:
workerThreads  要由線程池根據需要創建的新的最小工作程序線程數。

completionPortThreads  要由線程池根據需要創建的新的最小空閑異步 I/O 線程數。

使用這兩個方法可以控制線程池ThreadPool運行過程中的並發數量,他的效果怎么樣呢,我們寫段代碼來測試一下:

    public static void ThreadUseAndConstruction()
    {
        ThreadPool.SetMinThreads(5, 5); // 設置線程池最小線程數量為5
        ThreadPool.SetMaxThreads(15, 15); // 設置線程池最大線程數量為15
     
        Stopwatch watch = new Stopwatch();
        watch.Start();
     
        WaitCallback callback = index =>
        {
            Console.WriteLine(String.Format("{0}: Task {1} started", watch.Elapsed, index));
            Thread.Sleep(10000);
            Console.WriteLine(String.Format("{0}: Task {1} finished", watch.Elapsed, index));
        };
     
        for (int i = 0; i < 20; i++)
        {
            ThreadPool.QueueUserWorkItem(callback, i);
        }
    }

這段代碼部分執行結果如下

    00:00:00.0707416: Task 2 started
    00:00:00.0706114: Task 0 started
    00:00:00.0708271: Task 1 started
    00:00:00.0708882: Task 3 started
    00:00:00.0709376: Task 4 started
    00:00:01.0110528: Task 5 started
    00:00:01.5121437: Task 6 started
    00:00:02.0163181: Task 7 started
    00:00:02.5215778: Task 8 started
    00:00:03.0237865: Task 9 started
    00:00:03.5251736: Task 10 started
    00:00:04.0279218: Task 11 started
    00:00:04.5336314: Task 12 started
    00:00:05.0385531: Task 13 started
    00:00:06.0427984: Task 14 started
    00:00:10.0755285: Task 0 finished
    00:00:10.0755484: Task 3 finished
    00:00:10.0756457: Task 1 finished
    00:00:10.0756738: Task 15 started
    00:00:10.0756873: Task 16 started
    00:00:10.0755484: Task 4 finished
    00:00:10.0757537: Task 17 started
    00:00:10.0757709: Task 2 finished
    00:00:10.0757800: Task 18 started
    00:00:10.0758043: Task 19 started
    00:00:11.0137430: Task 5 finished
    00:00:11.5173026: Task 6 finished
    00:00:12.0214753: Task 7 finished
    00:00:12.5266871: Task 8 finished
    00:00:13.0289345: Task 9 finished
    00:00:13.5254343: Task 10 finished
    00:00:14.0330949: Task 11 finished
    00:00:14.5365363: Task 12 finished
    00:00:15.0412648: Task 13 finished
    00:00:16.0458671: Task 14 finished
    00:00:20.0808412: Task 15 finished
    00:00:20.0808480: Task 16 finished
    00:00:20.0808836: Task 17 finished
    00:00:20.0810045: Task 19 finished
    00:00:20.0810050: Task 18 finished

這段結果值得仔細分析,

1.程序在0秒的時候,瞬間創建出了5個線程,這看起來似乎和我們設置的最小線程數有關系

2.在第6-15個線程創建過程中,可以清晰的看到每個線程的創建時間幾乎都是間隔0.5秒

3.創建到第15個的時候,線程池停止創建新線程,直到10秒后有線程結束,才再次開始創建新線程,

4.從創建到第15個線程后,存活的線程一直保持在15個,直到所有線程都創建完成

從上述觀察結果不難得出,ThreadPool.SetMinThreads和ThreadPool.SetMaxThreads確實有控制線程池並發放量的功能。
 
另一個實驗:CLR線程池與IO線程池的存在和關聯

在閱讀一篇文章時(https://www.cnblogs.com/JeffreyZhao/archive/2009/10/20/thread-pool-3-lab.html),文中提到了一個CLR線程池與IO線程池的實驗,這個實驗探討了CLR線程池與IO線程池兩者的存在關系和關聯關系。即首先驗證了CLR線程池與IO線程池兩者是否真的存在,在線程調度過程中兩者是什么關系。

根據作者文中的實驗結果得出一個結論,IO線程池是真實存在的,並且和CLR線程池是相對獨立的。於是我也按照作者的方法進行了驗證:

    public static void IoThread()
    {
        ThreadPool.SetMinThreads(5, 3);
        ThreadPool.SetMaxThreads(5, 3);
     
        ManualResetEvent waitHandle = new ManualResetEvent(false);
     
        Stopwatch watch = new Stopwatch();
        watch.Start();
     
        WebRequest request = HttpWebRequest.Create("http://www.cnblogs.com/");
        request.BeginGetResponse(ar =>
        {
            var response = request.EndGetResponse(ar);
            Console.WriteLine(watch.Elapsed + ": Response Get");
     
        }, null);
     
        for (int i = 0; i < 10; i++)
        {
            ThreadPool.QueueUserWorkItem(index =>
            {
                Console.WriteLine(String.Format("{0}: Task {1} started", watch.Elapsed, index));
                waitHandle.WaitOne();
            }, i);
        }
     
        waitHandle.WaitOne();
    }

這段代碼通過對ThreadPool進行阻塞,判斷當ThreadPool被阻塞時,是否有其他線程池來完成未完成的IO操作,作者得出的結果是:

    00:00:00.0923543: Task 0 started
    00:00:00.1152495: Task 2 started
    00:00:00.1153073: Task 3 started
    00:00:00.1152439: Task 1 started
    00:00:01.0976629: Task 4 started
    00:00:01.5235481: Response Get

當啟動5個線程后,ThreadPool線程池被阻塞,但是仍然有一個Response Get被執行了,這說明在CLR線程池之外,應該還有一個獨立的IO線程池,在CLR線程池阻塞時仍然可以正常工作。

然而我的實驗結果卻是:

    00:00:00.0923543: Task 0 started
    00:00:00.1152495: Task 2 started
    00:00:00.1153073: Task 3 started
    00:00:00.1152439: Task 1 started
    00:00:01.0976629: Task 4 started

當CLR線程被阻塞后,並沒有Response Get被執行,考慮到可能是因為用的VS Code編譯器問題,畢竟在2019年VS Code for Mac +.Net Core這個組合還是一個新鮮的用法。於是我換了VS2017 for Mac + .Net Core 這套環境,重新執行了一次代碼,結果如下:

    00:00:00.3555712: Task 0 started
    00:00:00.3555281: Task 3 started
    00:00:00.3555586: Task 1 started
    00:00:00.3555431: Task 4 started
    00:00:00.3555174: Task 2 started
    00:00:00.4287879: Task 5 started
    00:00:01.5534256: Task 6 started
    00:00:01.5535319: Task 7 started
    00:00:02.5546253: Task 8 started
    00:00:02.5546795: Task 9 started
    00:00:03.7896417: Response Get

結果果然不一樣了,最后一個線程結束的時候出現了Response Get,但是發現10個線程全被調用了,於是我很奇怪,為何此時線程池不再阻塞,后來反復測試發現,如果ThreadPool.SetMaxThreads方法中第二個參數completionPortThreads小於等於3 時,此時線程池不會阻塞,completionPortThreads大於3 時,線程池會阻塞。

這更加讓我奇怪,我開始懷疑是不是因為我用的是Mac的原因,畢竟C#是微軟的產品,於是我換了台Windows電腦,在Net Core環境下再次執行,結果如下:

    00:00:00.0923543: Task 0 started
    00:00:00.1152495: Task 2 started
    00:00:00.1153073: Task 3 started
    00:00:00.1152439: Task 1 started
    00:00:01.0976629: Task 4 started

此時Windows+vs2017+.Net Core運行的結果和VS Code for Mac + .Net Core運行的結果是一樣的,即使ThreadPool.SetMaxThreads方法中第二個參數completionPortThreads小於等於3 時,線程池依然是阻塞的,並且傳說中的IO線程池也沒有顯示出他的存在感。

於是我又嘗試了最后一種組合,Windows + vs2017 + .Net Framework,此時的運行結果:

    00:00:00.0923543: Task 0 started
    00:00:00.1152495: Task 2 started
    00:00:00.1153073: Task 3 started
    00:00:00.1152439: Task 1 started
    00:00:01.0976629: Task 4 started
    00:00:01.5235481: Response Get

出現了和引用文章中作者一樣的結果。

到這里已經可以得出結論了:

1.引用文章中的作者測試環境是Windows + vs2017 + .Net Framework,在.Net Framework框架下,當CLR線程池被阻塞后,“可能”存在一個獨立的IO線程池,來繼續完成IO操作。之所以說可能,根據結果來看,並不能證明最后的寫IO操作是由IO線程池完成的。仍需要據需實驗測試。

2..Net Core和.Net Framework兩個框架中線程池的實現是有差異的。

3.有時候我們胸有成竹的實驗,結果往往並不具有普遍性,換了一個環境可能就會變的不適用。
————————————————
版權聲明:本文為CSDN博主「佚名兄」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/yangwohenmai1/article/details/90648566


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM