日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET6中的await原理浅析

發布時間:2023/11/16 asp.net 68 coder
生活随笔 收集整理的這篇文章主要介紹了 .NET6中的await原理浅析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

看過不少關于 await 的原理的文章,也知道背后是編譯器給轉成了狀態機實現的,但是具體是怎么完成的,回調又是如何銜接的,一直都沒有搞清楚,這次下定決心把源碼自己跑了下,終于豁然開朗了

本文的演示代碼基于 VS2022 + .NET 6

示例

public class Program
{
    static int Work()
    {
        Console.WriteLine("In Task.Run");
        return 1;
    }

    static async Task TestAsync()
    {
        Console.WriteLine("Before Task.Run");
        await Task.Run(Work);
        Console.WriteLine("After Task.Run");
    }

    static void Main()
    {
        _ = TestAsync();
        Console.WriteLine("End");
        Console.ReadKey();
    }
}
  • 很簡單的異步代碼,我們來看下,編譯器把它變成了啥
class Program
{
    static int Work()
    {
        Console.WriteLine("In Task.Run");
        return 1;
    }

    static Task TestAsync()
    {
        var stateMachine = new StateMachine()
        {
            _builder = AsyncTaskMethodBuilder.Create(),
            _state = -1
        };
        stateMachine._builder.Start(ref stateMachine);
        return stateMachine._builder.Task;
    }

    static void Main()
    {
        _ = TestAsync();
        Console.WriteLine("End");
        Console.ReadKey();
    }

    class StateMachine : IAsyncStateMachine
    {
        public int _state;
        public AsyncTaskMethodBuilder _builder;
        private TaskAwaiter<int> _awaiter;

        void IAsyncStateMachine.MoveNext()
        {
            int num = _state;
            try
            {
                TaskAwaiter<int> awaiter;
                if (num != 0)
                {
                    Console.WriteLine("Before Task.Run");
                    awaiter = Task.Run(Work).GetAwaiter();
                    if (!awaiter.IsCompleted)
                    {
                        _state = 0;
                        _awaiter = awaiter;
                        StateMachine stateMachine = this;
                        _builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
                        return;
                    }
                }
                else
                {
                    awaiter = _awaiter;
                    _awaiter = default;
                    _state = -1;
                }
                awaiter.GetResult();
                Console.WriteLine("After Task.Run");
            }
            catch (Exception exception)
            {
                _state = -2;
                _builder.SetException(exception);
                return;
            }
            _state = -2;
            _builder.SetResult();
        }

        void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { }
    }
}
  • 編譯后的代碼經過我的整理,命名簡化了,更容易理解

狀態機實現

  • 我們看到實際是生成了一個隱藏的狀態機類 StateMachine

  • 把狀態機的初始狀態 _state 設置 -1

  • stateMachine._builder.Start(ref stateMachine); 啟動狀態機,內部實際調用的就是狀態機的 MoveNext 方法

  • Task.Run 創建一個任務, 把委托放在 Task.m_action 字段,丟到線程池,等待調度

  • 任務在線程池內被調度完成后,是怎么回到這個狀態機繼續執行后續代碼的呢?
    _builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); 就是關鍵了, 跟下去,到了如下的代碼:

    if (!this.AddTaskContinuation(stateMachineBox, false))
    {
        ThreadPool.UnsafeQueueUserWorkItemInternal(stateMachineBox, true);
    }
    bool AddTaskContinuation(object tc, bool addBeforeOthers)
    {
        return !this.IsCompleted && ((this.m_continuationObject == null && Interlocked.CompareExchange(ref this.m_continuationObject, tc, null) == null) || this.AddTaskContinuationComplex(tc, addBeforeOthers));
    }
    
    • 這里很清楚的看到,嘗試把狀態機對象(實際是狀態機的包裝類),賦值到 Task.m_continuationObject, 如果操作失敗,則把狀態機對象丟進線程池等待調度,這里為什么這么實現,看一下線程池是怎么執行的就清楚了

線程池實現

  • .NET6 的線程池實現,實際是放到了 PortableThreadPool, 具體調試步驟我就不放了,直接說結果就是, 線程池線程從任務隊列中拿到任務后都執行了 DispatchWorkItem 方法
static void DispatchWorkItem(object workItem, Thread currentThread)
{
    Task task = workItem as Task;
    if (task != null)
    {
        task.ExecuteFromThreadPool(currentThread);
        return;
    }
    Unsafe.As<IThreadPoolWorkItem>(workItem).Execute();
}
virtual void ExecuteFromThreadPool(Thread threadPoolThread)
{
    this.ExecuteEntryUnsafe(threadPoolThread);
}
  • 我們看到, 線程池隊列中的任務都是 object 類型的, 這里進行了類型判斷, 如果是 Task , 直接執行 task.ExecuteFromThreadPool, 更有意思的這個方法是個虛方法,后面說明

  • ExecuteFromThreadPool 繼續追下去,我們來到了這里,代碼做了簡化

    private void ExecuteWithThreadLocal(ref Task currentTaskSlot, Thread threadPoolThread = null)
    {
        this.InnerInvoke();
        this.Finish(true);
    }
    
    virtual void InnerInvoke()
    {
        Action action = this.m_action as Action;
        if (action != null)
        {
            action();
            return;
        }
    }
    
  • 很明顯 this.InnerInvoke 就是執行了最開始 Task.Run(Work) 封裝的委托了, 在 m_action 字段

  • this.Finish(true); 跟下去會發現會調用 FinishStageTwo 設置任務的完成狀態,異常等, 繼續調用 FinishStageThree 就來了重點: FinishContinuations 這個方法就是銜接后續回調的核心

    internal void FinishContinuations()
    {
        object obj = Interlocked.Exchange(ref this.m_continuationObject, Task.s_taskCompletionSentinel);
        if (obj != null)
        {
            this.RunContinuations(obj);
        }
    }
    
  • 還記得狀態機實現么, Task.m_continuationObject 字段實際存儲的就是狀態機的包裝類,這里線程池線程也會判斷這個字段有值的話,就直接使用它執行后續代碼了

    void RunContinuations(object continuationObject)
    {
        var asyncStateMachineBox = continuationObject as IAsyncStateMachineBox;
        if (asyncStateMachineBox != null)
        {
            AwaitTaskContinuation.RunOrScheduleAction(asyncStateMachineBox, flag2);
            return;
        }
    }
    
    static void RunOrScheduleAction(IAsyncStateMachineBox box, bool allowInlining)
    {
        if (allowInlining && AwaitTaskContinuation.IsValidLocationForInlining)
        {
            box.MoveNext();
            return;
        }
    }
    

總結

  1. Task.Run 創建 Task, 把委托放在 m_action 字段, 把 Task 壓入線程池隊列,等待調度
  2. _builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); 嘗試把狀態機對象放在 Task.m_continuationObject 字段上,等待線程池線程調度完成任務后使用(用來執行后續),若操作失敗,直接把狀態機對象壓入線程池隊列,等待調度
  3. 線程池線程調度任務完成后,會判斷 Task.m_continuationObject 有值,直接執行它的 MoveNext

備注

  1. 狀態機實現中,嘗試修改 Task.m_continuationObject,可能會失敗,
    就會直接把狀態機對象壓入線程池, 但是線程池調度,不都是判斷是不是 Task 類型么, 其實狀態機的包裝類是 Task 的子類,哈哈,是不是明白了

    class AsyncStateMachineBox<TStateMachine> : Task<TResult>, IAsyncStateMachineBox where TStateMachine : IAsyncStateMachine
    
    static void DispatchWorkItem(object workItem, Thread currentThread)
    {
        Task task = workItem as Task;
        if (task != null)
        {
            task.ExecuteFromThreadPool(currentThread);
            return;
        }
        Unsafe.As<IThreadPoolWorkItem>(workItem).Execute();
    }
    
  • 還有就是狀態機包裝類,重寫了 Task.ExecuteFromThreadPool,所以線程池調用 task.ExecuteFromThreadPool 就是直接調用了狀態機的 MoveNext 了, Soga ^_^
    override void ExecuteFromThreadPool(Thread threadPoolThread)
    {
        this.MoveNext(threadPoolThread);
    }
    
參考鏈接
  • 關于線程池和異步的更深刻的原理,大家可以參考下面的文章

概述 .NET 6 ThreadPool 實現: https://www.cnblogs.com/eventhorizon/p/15316955.html

.NET Task 揭秘(2):Task 的回調執行與 await: https://www.cnblogs.com/eventhorizon/p/15912383.html

總結

以上是生活随笔為你收集整理的.NET6中的await原理浅析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。