LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

聊一聊 C#線程池的線程動(dòng)態(tài)注入

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

一:背景

1. 講故事

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

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

1. 降低GateThread的延遲時(shí)間

上一篇跟大家聊過(guò) Result 默認(rèn)情況下GateThread每秒會(huì)注入4個(gè),底層邏輯是由 Blocking.MaxDelayMs=250ms 變量控制的,言外之意就是能不能減少這個(gè)變量的值呢?當(dāng)然可以的,這里我們改成 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}: 這是耗時(shí)任務(wù)");
                    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();
        }

現(xiàn)在我們還是用上一篇的方法在如下三個(gè)方法 HasBlockingAdjustmentDelayElapsed,PerformBlockingAdjustment,CreateWorkerThread 上埋日志斷點(diǎn),埋好之后運(yùn)行程序觀察。

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

2. 提高 MinThreads 的閾值

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


        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}: 這是耗時(shí)任務(wù)");
                    Thread.Sleep(int.MaxValue);
                }, i);
            }
            Console.ReadLine();
        }

從卦中看,直接秒了這個(gè) 10000 個(gè)任務(wù),但不要忘了你的程序此時(shí)有1w個(gè)線程,如果是32bit程序大概率因?yàn)樘摂M地址不足直接崩了,如果是 64bit 可能也會(huì)導(dǎo)致非常可觀的內(nèi)存占用。

有些人可能對(duì)底層邏輯感興趣,我特意花了點(diǎn)時(shí)間繪了一張圖來(lái)描述底層的運(yùn)轉(zhuǎn)邏輯。

之所以能快速的產(chǎn)生新線程,核心判斷條件是 numProcessingWork <= counts.NumThreadsGoal ,我們?cè)O(shè)置的 MinThread=10000 最后給到了 NumThreadsGoal 字段,所以現(xiàn)有線程數(shù)不超過(guò) 10000 的話,就會(huì)不斷的調(diào)用 CreateWorkThread 產(chǎn)生新的工作線程。

接下來(lái)我們?cè)倭囊幌?SetMinThreads 這里面的坑吧,如果你將剛才的 ThreadPool.SetMinThreads(10000, 10); 改成 ThreadPool.SetMinThreads(10000, 10000);的話,將不會(huì)有任何效果,截圖如下:

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


        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 默認(rèn)最大值為 1000,如果你設(shè)置的值大于 1000 的話,那前面的 workerThreads 等于白設(shè)置了。。。這就很無(wú)語(yǔ)了。。。 如果參數(shù)有誤,你完全可以拋出一個(gè)異常來(lái)告訴我,,,而不是偷偷的掩埋錯(cuò)誤信息,導(dǎo)致程序出現(xiàn)了我意想不到的行為。。。

為了湊篇幅,我再說(shuō)一個(gè)有意思的參數(shù) DebugBreakOnWorkerStarvation,它可以用來(lái)捕獲 線程饑餓 的第一現(xiàn)場(chǎng),底層邏輯是C#團(tuán)隊(duì)在代碼里埋了一個(gè)鉤子,參考如下:


        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();
                }
            }
        }

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


        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}: 這是耗時(shí)任務(wù)");
                    Thread.Sleep(int.MaxValue);
                }, i);
            }
            Console.ReadLine();
        }

三:總結(jié)

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

轉(zhuǎn)自https://www.cnblogs.com/huangxincheng/p/18630175?


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

黄频国产免费高清视频,久久不卡精品中文字幕一区,激情五月天AV电影在线观看,欧美国产韩国日本一区二区
久久只有国产精品 | 亚洲aV免费一区二区三区 | 日本一区二区三区在线播放不卡 | 亚洲欧洲日本韩国精品 | 亚洲欧美自拍高清在线观看 | 亚洲一本大道中文在线 |