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

C#異步任務(wù)延續(xù)的三種底原理解析(最新推薦)

 更新時間:2025年05月12日 09:09:32   作者:一線碼農(nóng)  
這篇文章主要介紹了C#異步 任務(wù)延續(xù)的三種底層玩法,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧

一:背景

1. 講故事

最近聊了不少和異步相關(guān)的話題,有點疲倦了,今天再寫最后一篇作為近期這類話題的一個封筆吧,下篇繼續(xù)寫我熟悉的 生產(chǎn)故障 系列,突然親切感油然而生,哈哈,免費給別人看程序故障,是一種積陰德陽善的事情,欲知前世因,今生受者是。欲知來世果,今生做者是。

在任務(wù)延續(xù)方面,我個人的總結(jié)就是三類,分別為:

  • StateMachine
  • ContinueWith
  • Awaiter

話不多說,我們逐個研究下底層是咋玩的?

二:異步任務(wù)延續(xù)的玩法

1. StateMachine

說到狀態(tài)機大家再熟悉不過了,也是 async,await 的底層化身,很多人看到 async await 就想到了IO場景,其實IO場景和狀態(tài)機是兩個獨立的東西,狀態(tài)機是一種設(shè)計模式,把這個模式套在IO場景會讓代碼更加絲滑,僅此而已。為了方便講述,我們寫一個 StateMachine 與 IO場景 無關(guān)的一段測試代碼。

    internal class Program
    {
        static void Main(string[] args)
        {
            UseAwaitAsync();
            Console.ReadLine();
        }
        static async Task<string> UseAwaitAsync()
        {
            var html = await Task.Run(() =>
            {
                Thread.Sleep(1000);
                var response = "<html><h1>博客園</h1></html>";
                return response;
            });
            Console.WriteLine($"GetStringAsync 的結(jié)果:{html}");
            return html;
        }
    }

那這段代碼在底層是如何運作的呢?剛才也說到了asyncawait只是迷惑你的一種幻象,我們必須手握辟邪寶劍斬開幻象顯真身,這里借助 ilspy 截圖如下:

從卦中看,本質(zhì)上就是借助AsyncTaskMethodBuilder<string> 建造者將 awaiter 和 stateMachine 做了一個綁定,感興趣的朋友可以追一下 AwaitUnsafeOnCompleted() 方法,最后狀態(tài)機 <UseAwaitAsync>d__1 實例會放入到 Task.Run 的 m_continuationObject 字段。如果有朋友對流程比較蒙的話,我畫了一張簡圖。

圖和代碼都有了,接下來就是眼見為實。分別在 AddTaskContinuation 和 RunContinuations 方法中做好埋點,前者可以看到 延續(xù)任務(wù) 是怎么加進去的,后者可以看到 延續(xù)任務(wù) 是怎么取出來的。

心細的朋友會發(fā)現(xiàn)這卦上有一個很特別的地方,就是 allowInlining=true,也就是回調(diào)函數(shù)(StateMachine)是在當前線程上一擼到底的。

有些朋友可能要問,能不能讓延續(xù)任務(wù) 跑在單獨線程上? 可以是可以,但你得把 Task.Run 改成 Task.Factory.StartNew ,這樣就可以設(shè)置TaskCreationOptions參數(shù),參考代碼如下:

    var html = await Task.Factory.StartNew(() =>{}, TaskCreationOptions.RunContinuationsAsynchronously);

2. ContinueWith

那些同處于被裁的35歲大齡程序員應(yīng)該知道Task是 framework 4.0 時代出來的,而async,await是4.5出來的,所以在這個過渡期中有大量的項目會使用ContinueWith 導(dǎo)致回調(diào)地獄。。。 這里我們對比一下兩者有何不同,先寫一段參考代碼。

    internal class Program
    {
        static void Main(string[] args)
        {
            UseContinueWith();
            Console.ReadLine();
        }
        static Task<string> UseContinueWith()
        {
            var query = Task.Run(() =>
            {
                Thread.Sleep(1000);
                var response = "<html><h1>博客園</h1></html>";
                return response;
            }).ContinueWith(t =>
            {
                var html = t.Result;
                Console.WriteLine($"GetStringAsync 的結(jié)果:{html}");
                return html;
            });
            return query;
        }
    }

從卦代碼看確實沒有asyncawait簡潔,那 ContinueWith 內(nèi)部做了什么呢?感興趣的朋友可以跟蹤一下,本質(zhì)上和 StateMachine 的玩法是一樣的,都是借助 m_continuationObject 來實現(xiàn)延續(xù),畫個簡圖如下:

代碼和模型圖都有了,接下來就是用 dnspy 開干了。。。還是在 AddTaskContinuation 和 RunContinuations 上埋伏斷點觀察。

從卦中可以看到,延續(xù)任務(wù)使用新線程來執(zhí)行的,并沒有一擼到底,這明顯與 asyncawait 的方式不同,有些朋友可能又要說了,那如何實現(xiàn)和StateMachine一樣的呢?這就需要在 ContinueWith 中新增 ExecuteSynchronously 同步參數(shù),參考如下:

    var query = Task.Run(() => { }).ContinueWith(t =>
    {
    }, TaskContinuationOptions.ExecuteSynchronously);

3. Awaiter

使用Awaiter做任務(wù)延續(xù)的朋友可能相對少一點,它更多的是和 StateMachine 打配合,當然單獨使用也可以,但沒有前兩者靈活,它更適合那些不帶返回值的任務(wù)延續(xù),本質(zhì)上也是借助 m_continuationObject 字段實現(xiàn)的一套底層玩法,話不多說,上一段代碼:

        static Task<string> UseAwaiter()
        {
            var awaiter = Task.Run(() =>
            {
                Thread.Sleep(1000);
                var response = "<html><h1>博客園</h1></html>";
                return response;
            }).GetAwaiter();
            awaiter.OnCompleted(() =>
            {
                var html = awaiter.GetResult();
                Console.WriteLine($"UseAwaiter 的結(jié)果:{html}");
            });
            return Task.FromResult(string.Empty);
        }

前面兩種我配了圖,這里沒有理由不配了,哈哈,模型圖如下:

接下來把程序運行起來,觀察截圖:

從卦中觀察,它和StateMachine一樣,默認都是 一擼到底 的方式。

三:RunContinuations 觀察

這一小節(jié)我們單獨說一下 RunContinuations 方法,因為這里的實現(xiàn)太精妙了,不幸的是Dnspy和ILSpy反編譯出來的代碼太狗血,原汁原味的簡化后代碼如下:

    private void RunContinuations(object continuationObject) // separated out of FinishContinuations to enable it to be inlined
    {
        bool canInlineContinuations =
            (m_stateFlags & (int)TaskCreationOptions.RunContinuationsAsynchronously) == 0 &&
            RuntimeHelpers.TryEnsureSufficientExecutionStack();
        switch (continuationObject)
        {
            // Handle the single IAsyncStateMachineBox case.  This could be handled as part of the ITaskCompletionAction
            // but we want to ensure that inlining is properly handled in the face of schedulers, so its behavior
            // needs to be customized ala raw Actions.  This is also the most important case, as it represents the
            // most common form of continuation, so we check it first.
            case IAsyncStateMachineBox stateMachineBox:
                AwaitTaskContinuation.RunOrScheduleAction(stateMachineBox, canInlineContinuations);
                LogFinishCompletionNotification();
                return;
            // Handle the single Action case.
            case Action action:
                AwaitTaskContinuation.RunOrScheduleAction(action, canInlineContinuations);
                LogFinishCompletionNotification();
                return;
            // Handle the single TaskContinuation case.
            case TaskContinuation tc:
                tc.Run(this, canInlineContinuations);
                LogFinishCompletionNotification();
                return;
            // Handle the single ITaskCompletionAction case.
            case ITaskCompletionAction completionAction:
                RunOrQueueCompletionAction(completionAction, canInlineContinuations);
                LogFinishCompletionNotification();
                return;
        }
    }

卦中的 case 挺有意思的,除了本篇聊過的 TaskContinuation 和 IAsyncStateMachineBox 之外,還有另外兩種 continuationObject,這里說一下 ITaskCompletionAction 是怎么回事,其實它是 Task.Result 的底層延續(xù)類型,所以大家應(yīng)該能理解為什么 Task.Result 能喚醒,主要是得益于Task.m_continuationObject =completionAction 所致。

說了這么說,如何眼見為實呢?可以從源碼中尋找答案。

        private bool SpinThenBlockingWait(int millisecondsTimeout, CancellationToken cancellationToken)
        {
            var mres = new SetOnInvokeMres();
            AddCompletionAction(mres, addBeforeOthers: true);
            var returnValue = mres.Wait(Timeout.Infinite, cancellationToken);
        }
        private sealed class SetOnInvokeMres : ManualResetEventSlim, ITaskCompletionAction
        {
            internal SetOnInvokeMres() : base(false, 0) { }
            public void Invoke(Task completingTask) { Set(); }
            public bool InvokeMayRunArbitraryCode => false;
        }

從卦中可以看到,其實就是把 ITaskCompletionAction 接口的實現(xiàn)類 SetOnInvokeMres 塞入了 Task.m_continuationObject 中,一旦Task執(zhí)行完畢之后就會調(diào)用 Invoke() 下的 Set() 來實現(xiàn)事件喚醒。

四:總結(jié)

雖然異步任務(wù)延續(xù)有三種實現(xiàn)方法,但底層都是一個套路,即借助 Task.m_continuationObject 字段玩出的各種花樣,當然他們也是有一些區(qū)別的,即對 m_continuationObject 任務(wù)是否用單獨的線程調(diào)度,產(chǎn)生了不同的意見分歧。

到此這篇關(guān)于C#異步 任務(wù)延續(xù)的三種底層玩法的文章就介紹到這了,更多相關(guān)C#異步 任務(wù)延續(xù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談C#多線程下的調(diào)優(yōu)

    淺談C#多線程下的調(diào)優(yōu)

    本文主要介紹了C#多線程下的調(diào)優(yōu),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • C#4.0新特性之協(xié)變與逆變實例分析

    C#4.0新特性之協(xié)變與逆變實例分析

    這篇文章主要介紹了C#4.0新特性的協(xié)變與逆變,是比較實用的一個新功能,需要的朋友可以參考下
    2014-09-09
  • C#使用struct類型作為泛型Dictionary<TKey,TValue>的鍵

    C#使用struct類型作為泛型Dictionary<TKey,TValue>的鍵

    這篇文章介紹了C#使用struct類型作為泛型Dictionary<TKey,TValue>鍵值的方法,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • C#如何給新建的winform程序添加資源文件夾Resources

    C#如何給新建的winform程序添加資源文件夾Resources

    這篇文章主要介紹了C#如何給新建的winform程序添加資源文件夾Resources,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • C#實現(xiàn)簡單的計算器小程序

    C#實現(xiàn)簡單的計算器小程序

    這篇文章主要為大家詳細介紹了C#實現(xiàn)簡單的計算器小程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • c#中值類型和引用類型的基礎(chǔ)教程

    c#中值類型和引用類型的基礎(chǔ)教程

    這篇文章主要給大家介紹了關(guān)于c#中值類型和引用類型的基礎(chǔ)教程,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • c#動態(tài)執(zhí)行腳本的3種方式詳解

    c#動態(tài)執(zhí)行腳本的3種方式詳解

    本文主要介紹了c#動態(tài)執(zhí)行腳本的3種方式詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • C# 創(chuàng)建控制臺應(yīng)用程序

    C# 創(chuàng)建控制臺應(yīng)用程序

    這篇文章主要介紹了C# 創(chuàng)建控制臺應(yīng)用程序,在學(xué)習(xí)C#語言的時候,首先要學(xué)習(xí)控制臺的應(yīng)用程序,這樣才能專注于語言的學(xué)習(xí),減少學(xué)習(xí)的梯度,也有利于輸出自己需要輸出的內(nèi)容,一定要先使用控制臺的應(yīng)用程序的方式,下面就和小編一起學(xué)習(xí)該內(nèi)容吧
    2021-10-10
  • C#檢測移動硬盤并獲取移動硬盤盤符的方法

    C#檢測移動硬盤并獲取移動硬盤盤符的方法

    這篇文章主要介紹了利用C#檢測移動硬盤并獲取移動硬盤盤符
    2017-12-12
  • WinForm跨線程訪問UI及UI卡死的解決方案

    WinForm跨線程訪問UI及UI卡死的解決方案

    在WinForm開發(fā)過程中,跨線程訪問UI控件和界面卡死是常見的技術(shù)難題,由于Windows窗體應(yīng)用程序的UI控件默認只能在主線程(UI線程)上操作,直接在其他線程中修改UI會導(dǎo)致異常,本文通過實際測試案例,總結(jié)了Invoke和BeginInvoke在不同場景下的使用方法及注意事項
    2025-07-07

最新評論

漯河市| 莒南县| 江门市| 静海县| 铜山县| 二连浩特市| 黄浦区| 元氏县| 双柏县| 大冶市| 绥滨县| 兴安盟| 阳谷县| 平乡县| 洛宁县| 宣武区| 施秉县| 化德县| 新泰市| 上犹县| 嘉义市| 富锦市| 临沭县| 额敏县| 泰兴市| 富平县| 衡水市| 克东县| 四子王旗| 昭觉县| 丁青县| 时尚| 金乡县| 怀来县| 吉木乃县| 苏州市| 新巴尔虎左旗| 兴隆县| 綦江县| 嘉鱼县| 浑源县|