最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

.NET?Core?線程池(ThreadPool)底層原理源碼解析

 更新時間:2024年11月26日 09:12:38   作者:叫我安不理  
文章介紹了.NET?Core線程池的結(jié)構(gòu)和工作原理,包括生產(chǎn)者-消費(fèi)者模型、線程饑餓問題、線程池增長策略等,同時,對比了Task和線程池在并發(fā)編程中的優(yōu)缺點(diǎn),并推薦使用Task來優(yōu)化線程池的使用,感興趣的朋友一起看看吧

簡介

上文提到,創(chuàng)建線程在操作系統(tǒng)層面有4大無法避免的開銷。因此復(fù)用線程明顯是一個更優(yōu)的策略,切降低了使用線程的門檻,提高程序員的下限。

.NET Core線程池日新月異,不同版本實(shí)現(xiàn)都有差別,在.NET 6之前,ThreadPool底層由C++承載。在之后由C#承載。本文以.NET 8.0.8為藍(lán)本,如有出入,請參考源碼.

ThreadPool結(jié)構(gòu)模型圖

眼見為實(shí)

https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs上源碼 and windbg

internal sealed partial class ThreadPoolWorkQueue
{
        internal readonly ConcurrentQueue<object> workItems = new ConcurrentQueue<object>();//全局隊(duì)列
        internal readonly ConcurrentQueue<object> highPriorityWorkItems = new ConcurrentQueue<object>();//高優(yōu)先級隊(duì)列,比如Timer產(chǎn)生的定時任務(wù)
        internal readonly ConcurrentQueue<object> lowPriorityWorkItems =
            s_prioritizationExperiment ? new ConcurrentQueue<object>() : null!;//低優(yōu)先級隊(duì)列,比如回調(diào)
        internal readonly ConcurrentQueue<object>[] _assignableWorkItemQueues =
            new ConcurrentQueue<object>[s_assignableWorkItemQueueCount];//CPU 核心大于32個,全局隊(duì)列會分裂為好幾個,目的是降低CPU核心對全局隊(duì)列的鎖競爭
}

ThreadPool生產(chǎn)者模型

眼見為實(shí)

        public void Enqueue(object callback, bool forceGlobal)
        {
            Debug.Assert((callback is IThreadPoolWorkItem) ^ (callback is Task));
            if (_loggingEnabled && FrameworkEventSource.Log.IsEnabled())
                FrameworkEventSource.Log.ThreadPoolEnqueueWorkObject(callback);
#if CORECLR
            if (s_prioritizationExperiment)//lowPriorityWorkItems目前還是實(shí)驗(yàn)階段,CLR代碼比較偷懶,這一段代碼很不優(yōu)雅,沒有連續(xù)性。
            {
                EnqueueForPrioritizationExperiment(callback, forceGlobal);
            }
            else
#endif
            {
                ThreadPoolWorkQueueThreadLocals? tl;
                if (!forceGlobal && (tl = ThreadPoolWorkQueueThreadLocals.threadLocals) != null)
                {
                    tl.workStealingQueue.LocalPush(callback);//如果沒有特殊情況,默認(rèn)加入本地隊(duì)列
                }
                else
                {
                    ConcurrentQueue<object> queue =
                        s_assignableWorkItemQueueCount > 0 && (tl = ThreadPoolWorkQueueThreadLocals.threadLocals) != null
                            ? tl.assignedGlobalWorkItemQueue//CPU>32 加入分裂的全局隊(duì)列
                            : workItems;//CPU<=32 加入全局隊(duì)列
                    queue.Enqueue(callback);
                }
            }
            EnsureThreadRequested();
        }

細(xì)心的朋友,會發(fā)現(xiàn)highPriorityWorkItems的注入判斷哪里去了?目前CLR對于高優(yōu)先級隊(duì)列只開放給內(nèi)部,比如timer/Task使用

ThreadPool消費(fèi)者模型

眼見為實(shí)

public object? Dequeue(ThreadPoolWorkQueueThreadLocals tl, ref bool missedSteal)
        {
            // Check for local work items
            object? workItem = tl.workStealingQueue.LocalPop();
            if (workItem != null)
            {
                return workItem;
            }
            // Check for high-priority work items
            if (tl.isProcessingHighPriorityWorkItems)
            {
                if (highPriorityWorkItems.TryDequeue(out workItem))
                {
                    return workItem;
                }
                tl.isProcessingHighPriorityWorkItems = false;
            }
            else if (
                _mayHaveHighPriorityWorkItems != 0 &&
                Interlocked.CompareExchange(ref _mayHaveHighPriorityWorkItems, 0, 1) != 0 &&
                TryStartProcessingHighPriorityWorkItemsAndDequeue(tl, out workItem))
            {
                return workItem;
            }
            // Check for work items from the assigned global queue
            if (s_assignableWorkItemQueueCount > 0 && tl.assignedGlobalWorkItemQueue.TryDequeue(out workItem))
            {
                return workItem;
            }
            // Check for work items from the global queue
            if (workItems.TryDequeue(out workItem))
            {
                return workItem;
            }
            // Check for work items in other assignable global queues
            uint randomValue = tl.random.NextUInt32();
            if (s_assignableWorkItemQueueCount > 0)
            {
                int queueIndex = tl.queueIndex;
                int c = s_assignableWorkItemQueueCount;
                int maxIndex = c - 1;
                for (int i = (int)(randomValue % (uint)c); c > 0; i = i < maxIndex ? i + 1 : 0, c--)
                {
                    if (i != queueIndex && _assignableWorkItemQueues[i].TryDequeue(out workItem))
                    {
                        return workItem;
                    }
                }
            }
#if CORECLR
            // Check for low-priority work items
            if (s_prioritizationExperiment && lowPriorityWorkItems.TryDequeue(out workItem))
            {
                return workItem;
            }
#endif
            // Try to steal from other threads' local work items
            {
                WorkStealingQueue localWsq = tl.workStealingQueue;
                WorkStealingQueue[] queues = WorkStealingQueueList.Queues;
                int c = queues.Length;
                Debug.Assert(c > 0, "There must at least be a queue for this thread.");
                int maxIndex = c - 1;
                for (int i = (int)(randomValue % (uint)c); c > 0; i = i < maxIndex ? i + 1 : 0, c--)
                {
                    WorkStealingQueue otherQueue = queues[i];
                    if (otherQueue != localWsq && otherQueue.CanSteal)
                    {
                        workItem = otherQueue.TrySteal(ref missedSteal);
                        if (workItem != null)
                        {
                            return workItem;
                        }
                    }
                }
            }
            return null;
        }

什么是線程饑餓?

線程饑餓(Thread Starvation)是指線程長時間得不到調(diào)度(時間片),從而無法完成任務(wù)。

  • 線程被無限阻塞
    當(dāng)某個線程獲取鎖后長期不釋放,其它線程一直在等待
  • 線程優(yōu)先級降低
    操作系統(tǒng)鎖競爭中,高優(yōu)先級線程,搶占低優(yōu)先級線程的CPU時間
  • 線程在等待
    比如線程Wait/Result時,線程池資源不夠,導(dǎo)致得不到執(zhí)行

眼見為實(shí)

@一線碼農(nóng) 使用大佬的案例

http://m.fzitv.net/program/3313770o1.htm

http://m.fzitv.net/aspnet/3313810g7.htm

windbg sos bug依舊存在

大佬的文章中,描述sos存在bug,無法顯示線程堆積情況

經(jīng)實(shí)測,在.net 8中依舊存在此bug

99851個積壓隊(duì)列,沒有顯示出來

ThreadPool如何改善線程饑餓

CLR線程池使用爬山算法來動態(tài)調(diào)整線程池的大小來來改善線程饑餓的問題。本人水平有限,放出地址,有興趣的同學(xué)可以自行研究https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.HillClimbing.cs

ThreadPool如何增加線程

在 PortableThreadPool 中有一個子類叫 GateThread,它就是專門用來增減線程的類

其底層使用一個while (true) 每隔500ms來輪詢線程數(shù)量是否足夠,以及一個AutoResetEvent來接收注入線程Event.如果不夠就新增

《CLR vir C#》 一書中,提過一句 CLR線程池每秒最多新增1~2個線程。結(jié)論的源頭就是在這里注意:是線程池注入線程每秒1~2個,不是每秒只能創(chuàng)建1~2個線程。OS創(chuàng)建線程的速度塊多了。

眼見為實(shí)

https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs

眼見為實(shí)

        static void Main(string[] args)
        {
            for (int i = 0;i<=100000;i++)
            {
                ThreadPool.QueueUserWorkItem((x) =>
                {
                    Console.WriteLine($"當(dāng)前線程Id:{Thread.CurrentThread.ManagedThreadId}");
                    Thread.Sleep(int.MaxValue);
                });
            }
            Console.ReadLine();
        }

可以觀察輸出,判斷是不是每秒注入1~2個線程

Task

不用多說什么了吧?

Task的底層調(diào)用模型圖

Task的底層實(shí)現(xiàn)主要取決于TaskSchedule,一般來說,除了UI線程外,默認(rèn)是調(diào)度到線程池

眼見為實(shí)

Task.Run(() => { { Console.WriteLine("Test"); } });

其底層會自動調(diào)用Start(),Start()底層調(diào)用的TaskShedule.QueueTask().而作為實(shí)現(xiàn)類ThreadPoolTaskScheduler.QueueTask底層調(diào)用如下。

可以看到,默認(rèn)情況下(除非你自己實(shí)現(xiàn)一個TaskShedule抽象類).Task的底層使用ThreadPool來管理。

有意思的是,對于長任務(wù)(Long Task),直接是用一個單獨(dú)的后臺線程來管理,完全不參與調(diào)度。

Task對線程池的優(yōu)化

既然Task的底層是使用ThreadPool,而線程池注入速度是比較慢的。Task作為線程池的高度封裝,有沒有優(yōu)化呢?答案是Yes當(dāng)使用Task.Result時,底層會調(diào)用InternalWaitCore(),如果Task還未完成,會調(diào)用ThreadPool.NotifyThreadBlocked()來通知ThreadPool當(dāng)前線程已經(jīng)被阻塞,必須馬上注入一個新線程來代替被阻塞的線程。相對每500ms來輪詢注入線程,該方式采用事件驅(qū)動,注入線程池的速度會更快。

眼見為實(shí)

  static void Main(string[] args)
        {
            var client = new HttpClient();
            for(int i = 0; i < 100000; i++)
            {
                ThreadPool.QueueUserWorkItem(x =>
                {
                    Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} -> {x}: 這是耗時任務(wù)");
                    try
                    {
                        var content = client.GetStringAsync("https://youtube.com").Result;
                        Console.WriteLine(content);
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                });
            }
            Console.ReadLine();
        }

其底層通過AutoResetEvent來觸發(fā)注入線程的Event消息

結(jié)論

多用Task,它更完善。對線程池優(yōu)化更好。沒有不使用Task的理由

到此這篇關(guān)于.NET Core 線程池(ThreadPool)底層原理淺談的文章就介紹到這了,更多相關(guān).NET Core 線程池ThreadPool內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • .NET Core 1.0創(chuàng)建Self-Contained控制臺應(yīng)用

    .NET Core 1.0創(chuàng)建Self-Contained控制臺應(yīng)用

    這篇文章主要為大家詳細(xì)介紹了.NET Core 1.0創(chuàng)建Self-Contained控制臺應(yīng)用的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • Visual?Studio?2022常見的報錯以及處理方案圖文詳解

    Visual?Studio?2022常見的報錯以及處理方案圖文詳解

    許多用戶在使用Visual Studio的過程中常會遇到各種問題,下面這篇文章主要給大家介紹了關(guān)于Visual?Studio?2022常見的報錯以及處理方案的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-04-04
  • 詳解ASP.NET Core 中基于工廠的中間件激活的實(shí)現(xiàn)方法

    詳解ASP.NET Core 中基于工廠的中間件激活的實(shí)現(xiàn)方法

    這篇文章主要介紹了ASP.NET Core 中基于工廠的中間件激活的實(shí)現(xiàn)方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • WebApi2 文件圖片上傳與下載功能

    WebApi2 文件圖片上傳與下載功能

    這篇文章主要介紹了WebApi2 文件圖片上傳與下載功能,需要的朋友可以參考下
    2017-05-05
  • 淺談.net平臺下深拷貝和淺拷貝

    淺談.net平臺下深拷貝和淺拷貝

    在.net類庫中,對象克隆廣泛存在于各種類型的實(shí)現(xiàn)中,凡是實(shí)現(xiàn)了ICloneable接口的類型都具備克隆其對象實(shí)例的能力。所以本文講述的深拷貝和淺拷貝也是在實(shí)現(xiàn)ICloneable接口的基礎(chǔ)上進(jìn)行的
    2013-12-12
  • 詳解如何在ASP.NET Core中應(yīng)用Entity Framework

    詳解如何在ASP.NET Core中應(yīng)用Entity Framework

    本篇文章主要介紹了如何在ASP.NET Core中應(yīng)用Entity Framework,具有一定的參考價值,有興趣的可以一起了解一下。
    2016-12-12
  • ASP.NET 多附件上傳實(shí)現(xiàn)代碼

    ASP.NET 多附件上傳實(shí)現(xiàn)代碼

    在寫這篇文章之前我也在Google上找到了很多有關(guān)多附件上傳的文章,有用ASP.NET實(shí)現(xiàn)的,也有用JSP、PHP等其它技術(shù)實(shí)現(xiàn)的
    2009-06-06
  • asp.net窗體的打開和關(guān)閉(輸出js)

    asp.net窗體的打開和關(guān)閉(輸出js)

    asp.net窗體的打開和關(guān)閉(輸出js),需要的朋友可以參考下。
    2011-06-06
  • .net core如何利用ConcurrentTest組件對方法進(jìn)行壓力測試詳解

    .net core如何利用ConcurrentTest組件對方法進(jìn)行壓力測試詳解

    這篇文章主要給大家介紹了關(guān)于.net core如何利用ConcurrentTest組件對方法進(jìn)行壓力測試的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧
    2018-11-11
  • 詳解如何在ASP.NET Core中使用IHttpClientFactory

    詳解如何在ASP.NET Core中使用IHttpClientFactory

    這篇文章主要介紹了詳解如何在ASP.NET Core中使用IHttpClientFactory,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02

最新評論

宣威市| 东至县| 本溪| 鄢陵县| 电白县| 车致| 五家渠市| 台北市| 类乌齐县| 河津市| 阿图什市| 子洲县| 清丰县| 苗栗市| 甘洛县| 蓝山县| 辽宁省| 延寿县| 阿瓦提县| 柳江县| 霍邱县| 稷山县| 诏安县| 徐水县| 龙州县| 舟曲县| 随州市| 马边| 双城市| 海阳市| 比如县| 本溪市| 淮南市| 旬邑县| 盖州市| 烟台市| 天门市| 冀州市| 濮阳县| 芦山县| 佛坪县|