C#異步原理詳情

前言:

async關鍵字和await表達式表達的異步操作在C#5便發佈瞭,其使用簡單,讓開發者能夠用同步的方式來書寫異步代碼,真的很棒。當然,編譯器在後面也做瞭不少工作–一個異步方法本質上是被編譯器轉換為一個樁方法和一個狀態機。隨著C#版本的不斷更新,可能編譯器轉換後的代碼有所變化,但本質的東西應該不會變太多。這篇筆記來源於C# in depth(第四版),記錄一些關鍵的地方,便於自己記憶,希望對你也有所幫助。

文章結論主要以下五點:

  • (1)使用builder作為異步基礎架構,async方法會被轉換成樁方法和狀態機。
  • (2)狀態機會追蹤builder、方法參數、局部變量、awaiter以及續延中需要恢復執行的位置。
  • (3)編譯器會創建一些代碼,旨在在方法恢復時回到方法內部。
  • (4)INotifyCompletion和ICriticalNotifyCompletion接口可用於控制執行上下文的貫穿。
  • (5)builder方法由編譯器負責調用。

下面我們就上面的5點展開說明。

一、關於第一點的說明

我們編寫的async方法會被編譯器編譯為一個樁方法和一個狀態機。

樁方法的簽名和async方法的簽名一致。在樁方法的內部,會首先new一個狀態機,然後初始化這個狀態機,初始化的過程主要包含

①狀態機的狀態字段(用於記錄await表達式完成後的恢復執行處)、

builderAsyncTaskMethodBuilder)的初始化、

③捕獲async方法參數後提升為字段的值。狀態機初始化好之後,就要調用狀態機的builder字段(一般情況下是AsyncTaskMethodBuilder)的Start方法,之後返回builder字段的Task屬性。在這個過程中要註意狀態機和builder都是一個值類型,所以在builder上執行的Start參數有ref修飾符,表示按引用傳遞,原因是在await方法回調後值類型的狀態能夠保存。

至於async方法中的執行邏輯,則全部被轉移到瞭狀態機的MoveNext方法中。

樁方法和狀態機之間的聯系就是通過builder來建立的。

二、關於第二點的說明

狀態機會保存builder的一個字段,async方法的參數也會被提升為狀態機的字段,如果在async方法的await表達式之後需要訪問async方法中的局部變量,也需要將該局部變量保存到狀態機中。狀態機還保存瞭awaiter字段,一般情況下,不同類型的await表達式隻保存一個就行。至於await之後續延中需要恢復執行的位置由一個state的字段來表示,當state字段的值為-1時表示為執行或正在執行,大於0時表示暫定,-2表示已經結束,結束表示正常完成或有異常。

三、關於第三點的說明

狀態機中創建瞭大量的樣板代碼來“翻譯”async方法中的代碼,一般我們通過反編譯工具可以看到大量的switchgoto之類的語句。

//MoveNext方法的樣板代碼
void IAsyncStateMachine. MoveNext() {
    try 
       { 
          switch (this. state)
         { 
            default: goto MethodStart; 
            case 0: goto Label0A; 
            case 1: goto Label1A; 
            case 2: goto Label2A; <------ case 的數量與await表達式數量相等
          }

          MethodStart: <------ 第一個await表達式之前的代碼 
                                          <------ 設置第一個awaiter 
          Label0A: <------ 從續延中恢復執行的代碼 
          Label0B: <------ 快速路徑和慢速路徑匯合之處 
                        <------ 剩餘代碼,包括更多標簽以及awaiter等 
         } 
          catch (Exception e) //(本行及以下5行) 通過builder填充所有異常信息 
         { 
            this.state = -2; 
            builder.SetException(e);
            return; 
          } 
          this.state = -2; //(本行及以下1行)通過builder填充方法完成的信息 
          builder.SetResult(); 
}

四、關於第四點的說明

ICriticalNotifyCompletion可以在基礎框架配合下實現執行上下文(回調)的安全訪問,INotifyCompletion需要通過ExecutionContext類來配合完成(ExecutionContext的Capture和Run)。這兩個接口對應builder的AwaitOnCompleted<TAwaiter,TStateMachine>(ref TAwaiterawaiter,ref TStateMachinestateMachine)方法和AwaitUnsafeOnCompleted<TAwaiter,TStateMachine>(ref TAwaiterawaiter,ref TStateMachinestateMachine)方法,也會被後者進行調用。(builder具體要使用哪一個方法要看Task實現瞭哪一個接口)

五、關於第五點的說明

builder和狀態機等等都是編譯器生成的,至於調用麼,當然得由編譯器負責調用瞭。

到此這篇關於C#異步原理詳情的文章就介紹到這瞭,更多相關C#異步原理內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: