欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

聊一聊 C#線程池的線程動態注入

freeflydom
2025年1月3日 9:50 本文熱度 2080

一:背景

1. 講故事

前面二篇我們聊到了 Thread.Sleep 和 Task.Result 場景下的線程注入邏輯,在線程饑餓的情況下注入速度都不是很理想,那怎么辦呢?有沒有更快的注入速度,這篇作為 動態注入 的終結篇,我個人總結如下兩種方法,當然可能有更多的路子,知道的朋友可以在下面留言。

二:提高注入速度的兩種方法

1. 降低GateThread的延遲時間

上一篇跟大家聊過 Result 默認情況下GateThread每秒會注入4個,底層邏輯是由 Blocking.MaxDelayMs=250ms 變量控制的,言外之意就是能不能減少這個變量的值呢?當然可以的,這里我們改成 100ms,參考代碼如下:


        static void Main(string[] args)
        {
            AppContext.SetData("System.Threading.ThreadPool.Blocking.MaxDelayMs", 100);
            for (int i = 0; i < 10000; i++)
            {
                ThreadPool.QueueUserWorkItem((idx) =>
                {
                    Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} -> {idx}: 這是耗時任務");
                    try
                    {
                        var client = new HttpClient();
                        var content = client.GetStringAsync("https://youtube.com").Result;
                        Console.WriteLine(content.Length);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }, i);
            }
            Console.ReadLine();
        }

現在我們還是用上一篇的方法在如下三個方法 HasBlockingAdjustmentDelayElapsed,PerformBlockingAdjustment,CreateWorkerThread 上埋日志斷點,埋好之后運行程序觀察。

從卦中的輸出結果看,注入速度明顯快了很多,判斷閾值也從 250ms 變成了 100ms,每秒能注入7~8個線程,所以這是一個簡單粗暴的提速方法。

2. 提高 MinThreads 的閾值

看過上兩篇的朋友應該知道,我用過 噴涌而出 四個字來形容前 12個線程,這里的12是因為我的機器是 12 核,言外之意就是為什么要設置12呢?我能不能給它提升到 120,1200甚至更高的 12000 呢?這樣線程的注入速度不是更快嗎?有了這個想法趕緊上一段代碼,參考如下:


        static void Main(string[] args)
        {
            ThreadPool.SetMinThreads(10000, 10);
            for (int i = 0; i < 10000; i++)
            {
                ThreadPool.QueueUserWorkItem((idx) =>
                {
                    Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} -> {idx}: 這是耗時任務");
                    Thread.Sleep(int.MaxValue);
                }, i);
            }
            Console.ReadLine();
        }

從卦中看,直接秒了這個 10000 個任務,但不要忘了你的程序此時有1w個線程,如果是32bit程序大概率因為虛擬地址不足直接崩了,如果是 64bit 可能也會導致非常可觀的內存占用。

有些人可能對底層邏輯感興趣,我特意花了點時間繪了一張圖來描述底層的運轉邏輯。

之所以能快速的產生新線程,核心判斷條件是 numProcessingWork <= counts.NumThreadsGoal ,我們設置的 MinThread=10000 最后給到了 NumThreadsGoal 字段,所以現有線程數不超過 10000 的話,就會不斷的調用 CreateWorkThread 產生新的工作線程。

接下來我們再聊一下 SetMinThreads 這里面的坑吧,如果你將剛才的 ThreadPool.SetMinThreads(10000, 10); 改成 ThreadPool.SetMinThreads(10000, 10000);的話,將不會有任何效果,截圖如下:

為什么會出現這樣的情況呢?這得從源碼上找答案,參考代碼如下:


        public class PortableThreadPool
        {
            private short _minThreads;
            private short _maxThreads;
            private short _legacy_maxIOCompletionThreads;
            private const short DefaultMaxWorkerThreadCount = MaxPossibleThreadCount;
            private const short MaxPossibleThreadCount = short.MaxValue;
            private PortableThreadPool()
            {
                _minThreads = HasForcedMinThreads ? ForcedMinWorkerThreads : (short)Environment.ProcessorCount;
                _maxThreads = HasForcedMaxThreads ? ForcedMaxWorkerThreads : DefaultMaxWorkerThreadCount;
                _legacy_maxIOCompletionThreads = 1000;
            }
        }
        public bool SetMinThreads(int workerThreads, int ioCompletionThreads)
        {
            if (workerThreads < 0 || ioCompletionThreads < 0)
            {
                return false;
            }
            bool flag = false;
            bool flag2 = false;
            this._threadAdjustmentLock.Acquire();
            if (workerThreads > (int)this._maxThreads)
            {
                return false;
            }
            if (ioCompletionThreads > (int)this._legacy_maxIOCompletionThreads)
            {
                return false;
            }
        }

從卦中代碼可以看到 ioCompletionThreads 默認最大值為 1000,如果你設置的值大于 1000 的話,那前面的 workerThreads 等于白設置了。。。這就很無語了。。。 如果參數有誤,你完全可以拋出一個異常來告訴我,,,而不是偷偷的掩埋錯誤信息,導致程序出現了我意想不到的行為。。。

為了湊篇幅,我再說一個有意思的參數 DebugBreakOnWorkerStarvation,它可以用來捕獲 線程饑餓 的第一現場,底層邏輯是C#團隊在代碼里埋了一個鉤子,參考如下:


        private static void GateThreadStart()
        {
            bool debuggerBreakOnWorkStarvation = AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.DebugBreakOnWorkerStarvation", false);
            while (counts.NumProcessingWork < threadPoolInstance._maxThreads && counts.NumProcessingWork >= counts.NumThreadsGoal)
            {
                if (debuggerBreakOnWorkStarvation)
                {
                    Debugger.Break();
                }
            }
        }

這個 Debugger.Break(); 發出的 int 3 信號,我們可以用 VS,DnSpy,WinDbg 這樣的調試器去捕獲,參考代碼如下:


        static void Main(string[] args)
        {
            AppContext.SetSwitch("System.Threading.ThreadPool.DebugBreakOnWorkerStarvation", true);
            for (int i = 0; i < 10000; i++)
            {
                ThreadPool.QueueUserWorkItem((idx) =>
                {
                    Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} -> {idx}: 這是耗時任務");
                    Thread.Sleep(int.MaxValue);
                }, i);
            }
            Console.ReadLine();
        }

三:總結

我們聊到了兩種提升線程注入的方法,尤其是第二種讓人意難平,面對上游洪水猛獸般的對線程池進行DDOS攻擊,下游的線程不顧一切,傾家蕩產的去承接,這是一種明知不可為而為之的悲壯之舉。

轉自https://www.cnblogs.com/huangxincheng/p/18630175?


該文章在 2025/1/3 9:50:48 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved