多線程之旅(ThreadPool 線程池)


一、什么是ThreadPool 線程池(源碼

      1.線程池顧名思義,有我們的系統創建一個容器裝載着我們的線程,由CLR控制的所有AppDomain共享。線程池可用於執行任務、發送工作項、處理異步 I/O、代表其他線程等待以及處理計時器。所以使用線程池不需要自己創建線程,而是通過線程池來創建和執行和管理線程。

二、ThreadPool 線程池和線程的區別

      1.ThreadPool 線程池是在.NET 2.0出現的,是一個享元模式整個程序共同享用這一個線程池,當我們的線程執行任務之后它不會立刻銷毀,它會回到線程池中,如果有新的任務它就會去執行。避免了我們線程的重復創建和銷毀(也不會造成我們CPU的上下文切換的損耗)。

      2.大家仔細看一下我前面寫的Thread 創建線程執行任務之后,它會自動銷毀。那問題來了我們經常的創建、銷毀線程這可都是資源的浪費呀!!所以我們要利用每個線程占有的資源。

三、ThreadPool 線程池缺點

       1.線程池在性能上優於線程,但是它也是有缺點的。它不支持線程的取消、完成、失敗通知等交互性操作。

       2.它不能設置池中線程的Name,會增加使用者的難度。

  3.線程池中線程通常都是后台線程,優先級為ThreadPriority.Normal

  4.線程池堵塞會影響我們的性能,阻塞會使CLR錯誤地認為它占用了大量CPU。CLR能夠檢測或補償(往池中注入更多線程),但是這可能使線程池受到后續超負荷的印象。Task (也及時后面要講的)解決了這個問題。

  5.線程池使用的是全局隊列,全局隊列中的線程依舊會存在競爭共享資源的情況,從而影響性能(Task 解決了這個問題,方案是使用本地隊列)。

四、線程池工作原理

  1.CLR初始化時,線程池中是沒有線程的,但是內部有一個操作請求隊列,當我們的應用程序使用異步時,會將一個記錄項添加到線程池的隊列中,線程池隊列會自動讀取這個記錄項,並且發給一個線程池的線程,如果線程池沒有線程就會創建一個線程執行這任務,當線程完成任務它不會自動銷毀而是回到我們的線程池中,等待線程池派發新的任務。

  2.如果程序給線程池派發了很多任務,線程池也會使用這一個線程執行所有的任務,如果我們的請求速度大於了我們的線程處理速度,就會創建額外線程,就不會導致我們創建了過多的線程。

  2.當我們的線程有大量休息的,它們會在一段時間內自動銷毀。這樣很好的控制了我們應用程序的性能。

五、ThreadPool 線程池使用

  1.ThreadPool是一個靜態類,調用QueueUserWorkItem方法,是可以將一個異步計算放入我們的線程池隊列中。

public static bool QueueUserWorkItem(WaitCallback callBack);
public static bool QueueUserWorkItem(WaitCallback callBack, object state);
方法 說明
QueueUserWorkItem 啟動線程池里的一個線程(工作者線程)
GetMinThreads 檢索線程池在新請求預測中能夠按需創建的線程的最小數量。
GetMaxThreads 最多可用線程數,所有大於此數目的請求將保持排隊狀態,直到線程池線程由空閑。
GetAvailableThreads 剩余空閑線程數。
SetMaxThreads 設置線程池中的最大線程數(請求數超過此值則進入隊列)。
SetMinThreads 設置線程池最少需要保留的線程數。

 

 

 

 

 

 

  2.我們可以看到ThreadPool比Thread少了很多的API,被砍掉了

/// <summary>
        /// ThreadPool的使用
        /// workerThreads  CLR線程池分為工作者線程(workerThreads)
        /// completionPortThreads I/O線程(completionPortThreads)
        /// </summary>
        public static void Show()
        {
            //使用線程
            ThreadPool.QueueUserWorkItem((x) => Running());
            ThreadPool.GetAvailableThreads(out int workerThreads, out int completionPortThreads);
            Console.WriteLine($"沒有設置線程數之前 workerThreads:{workerThreads}  completionPortThreads:{completionPortThreads}");
            //設置最大的線程數
            ThreadPool.SetMaxThreads(16, 16);
            //設置最小的線程數
            ThreadPool.SetMinThreads(8, 8);
            ThreadPool.GetAvailableThreads(out int workerThreads1, out int completionPortThreads1);
            Console.WriteLine($"設置最大的線程數之后 workerThreads:{workerThreads1}  completionPortThreads:{completionPortThreads1}");
            Console.ReadLine();
        }
View Code

   3.我們要注意的就是堵塞線程的時候一定要做好處理,最好是不要堵塞我們的線程,不然很容易造成死鎖GG

        /// <summary>
        /// ThreadPool 線程等待
        ///類  包含了一個bool屬性
        ///false--WaitOne等待--Set--true--WaitOne直接過去
        ///true--WaitOne直接過去--ReSet--false--WaitOne等待
        ///https://www.cnblogs.com/howtrace/p/11362284.html
        /// </summary>
        public static void Show1()
        {
            //設置最大的線程數
            ThreadPool.SetMaxThreads(16, 16);
            //設置最小的線程數
            ThreadPool.SetMinThreads(8, 8);
            //設置false使用WaitOne()會直接堵塞線程,不會釋放 、Set()設置為true
            ManualResetEvent manualResetEvent = new ManualResetEvent(false);
            //設置false使用WaitOne()會直接堵塞線程,不會釋放 、Set()設置為true
            AutoResetEvent autoResetEvent = new AutoResetEvent(false);
            //上面兩種方法都是可以攔截線程,都是繼承EventWaitHandle 接口
            //就都具有Reset() //紅燈 設置為false導致線程等待
            //Set() //綠燈 設置為true 啟動線程繼續執行
            //WaitOne() // 等待信號 會根據我們線程狀態執行,為true不需要等待直接執行
            //反之為false會等待線程狀態為true才會執行

            //不同點 ManualResetEvent AutoResetEvent
            //ManualResetEvent 在·使用Set()的時候會所有處理 WaitOne 狀態線程均繼續執行。
            //AutoResetEvent 在使用Set()的時候會執行一個線程其他的線程繼續等待執行。

            for (int i = 0; i < 20; i++)
            {
                var k = i;
                ThreadPool.QueueUserWorkItem(x =>
                {
                    Console.WriteLine(k);
                    if (k < 18)
                    {
                        //等待線程,但是上面我們只開了16個線程,結果我18個線程全部等待
                        //導致了死鎖
                        manualResetEvent.WaitOne();
                    }
                    else
                    {
                        //恢復執行狀態
                        manualResetEvent.Set();
                    }
                });
                if (manualResetEvent.WaitOne())
                {
                    Console.WriteLine("沒有死鎖、、、");
                }
                Console.WriteLine("等着QueueUserWorkItem完成后才執行");
            }
            Console.ReadLine();
        }
View Code


免責聲明!

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



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