1. 程式人生 > >15.5.2 【Task實現細節】骨架方法的結構

15.5.2 【Task實現細節】骨架方法的結構

nsa 你會 move res condition trying 異步 .get fault

  盡管骨架方法中的代碼非常簡單,但它暗示了狀態機的職責。代碼清單15-11生成的骨架方 法如下所示:

 1         [DebuggerStepThrough]
 2         [AsyncStateMachine(typeof(DemoStateMachine))]
 3         static Task<int> SumCharactersAsync(IEnumerable<char> text)
 4         {
 5             var machine = new DemoStateMachine();
 6
machine.text = text; 7 machine.builder = AsyncTaskMethodBuilder<int>.Create(); 8 machine.state = -1; 9 machine.builder.Start(ref machine); 10 return machine.builder.Task; 11 }

  AsyncStateMachineAttribute 類型是為 async 引入的新特性(attribute)之一。它是為工具而設計的,你自己並不會有機會消費這個特性,並且也不應該在自己的方法上應用這個特性。

我們已經在這個狀態機上看到了三個字段。
? 一個是參數( text )。顯然有多少個參數就會有多少個字段。
? 一個是 AsyncTaskMethodBuilder<int> 。該結構負責將狀態機和骨架方法聯系在一起。對於僅返回 Task 的方法,存在對應的非泛型類。對於返回 void 的方法,可以使用AsnycVoidMethodBuilder 結構。
? 一個是 state ,值從 -1 開始。初始值永遠為 -1 ,稍後我們會介紹其他值的含義。

  由於狀態機是一個結構(struct), AsyncTaskMethodBuilder<int> 也是一個結構,因此我 們還沒有執行任何堆分配。當然,完全可以讓執行的不同調用在堆上進行分配,但有必要指出的 是,代碼在盡可能地避免這麽做。異步的本質意味著,如果哪個 await 表達式需要真正的等待, 你會需要很多這種值(在堆上),但代碼保證了它們只會在需要的時候進行裝箱。所有這些都屬 於實現細節,就像堆和棧屬於實現細節一樣,但為了讓 async 能夠適用於盡可能多的場景,微軟 的相關團隊緊密合作,將分配降低到了絕對最小值。

  對 machine.builder.Start(ref machine) 的調用非常有意思。這裏使用了按引用傳遞, 以避免創建狀態機的復本(以及builder的復本),這是出於性能和正確性兩方面的考慮。編譯器 非常願意將狀態機和builder視為類,因此 ref 可以在代碼中自由地使用。為了使用接口,不同的 方法將builder(或awaiter)作為參數,使用泛型類型參數,並限定其實現某個接口(如對於狀態 機來說就是 IAsyncStateMachine )。這樣在調用接口的成員時,就不需要任何裝箱了。方法的 行為描述起來非常簡單——它讓狀態機同步地執行第一個步驟,並在方法完成時或到達需等待的 異步操作點時得以返回。

  第一個步驟完成後,骨架方法將返回builder中的任務。狀態機在結束時,會使用builder來設 置結果或異常。

  1     class DecompilationSampleDecompiled
  2     {
  3         static void Main()
  4         {
  5             Task<int> task = SumCharactersAsync("test");
  6             Console.WriteLine(task.Result);
  7         }
  8 
  9         [DebuggerStepThrough]
 10         [AsyncStateMachine(typeof(DemoStateMachine))]
 11         static Task<int> SumCharactersAsync(IEnumerable<char> text)
 12         {
 13             var machine = new DemoStateMachine();
 14             machine.text = text;
 15             machine.builder = AsyncTaskMethodBuilder<int>.Create();
 16             machine.state = -1;
 17             machine.builder.Start(ref machine);
 18             return machine.builder.Task;
 19         }
 20 
 21         [CompilerGenerated]
 22         private struct DemoStateMachine : IAsyncStateMachine
 23         {
 24             // Fields for parameters
 25             public IEnumerable<char> text;
 26 
 27             // Fields for local variables
 28             public IEnumerator<char> iterator;
 29             public char ch;
 30             public int total;
 31             public int unicode;
 32 
 33             // Fields for awaiters
 34             private TaskAwaiter taskAwaiter;
 35             private YieldAwaitable.YieldAwaiter yieldAwaiter;
 36 
 37             // Common infrastructure
 38             public int state;
 39             public AsyncTaskMethodBuilder<int> builder;
 40             private object stack;
 41 
 42             void IAsyncStateMachine.MoveNext()
 43             {
 44                 int result = default(int);
 45                 try
 46                 {
 47                     bool doFinallyBodies = true;
 48                     switch (state)
 49                     {
 50                         case -3:
 51                             goto Done;
 52                         case 0:
 53                             goto FirstAwaitContinuation;
 54                         case 1:
 55                             goto SecondAwaitContinuation;
 56                     }
 57                     // Default case - first call (state is -1)
 58                     total = 0;
 59                     iterator = text.GetEnumerator();
 60 
 61                 // We really want to jump straight to FirstAwaitRealContinuation, but we can‘t
 62                 // goto a label inside a try block...
 63                 FirstAwaitContinuation:
 64                     // foreach loop
 65                     try
 66                     {
 67                         // for/foreach loops typically have the condition at the end of the generated code.
 68                         // We want to go there *unless* we‘re trying to reach the first continuation.
 69                         if (state != 0)
 70                         {
 71                             goto LoopCondition;
 72                         }
 73                         goto FirstAwaitRealContinuation;
 74                     LoopBody:
 75                         ch = iterator.Current;
 76                         unicode = ch;
 77                         TaskAwaiter localTaskAwaiter = Task.Delay(unicode).GetAwaiter();
 78                         if (localTaskAwaiter.IsCompleted)
 79                         {
 80                             goto FirstAwaitCompletion;
 81                         }
 82                         state = 0;
 83                         taskAwaiter = localTaskAwaiter;
 84                         builder.AwaitUnsafeOnCompleted(ref localTaskAwaiter, ref this);
 85                         doFinallyBodies = false;
 86                         return;
 87                     FirstAwaitRealContinuation:
 88                         localTaskAwaiter = taskAwaiter;
 89                         taskAwaiter = default(TaskAwaiter);
 90                         state = -1;
 91                     FirstAwaitCompletion:
 92                         localTaskAwaiter.GetResult();
 93                         localTaskAwaiter = default(TaskAwaiter);
 94                         total += unicode;
 95                     LoopCondition:
 96                         if (iterator.MoveNext())
 97                         {
 98                             goto LoopBody;
 99                         }
100                     }
101                     finally
102                     {
103                         if (doFinallyBodies && iterator != null)
104                         {
105                             iterator.Dispose();
106                         }
107                     }
108 
109                     // After the loop
110                     YieldAwaitable.YieldAwaiter localYieldAwaiter = Task.Yield().GetAwaiter();
111                     if (localYieldAwaiter.IsCompleted)
112                     {
113                         goto SecondAwaitCompletion;
114                     }
115                     state = 1;
116                     yieldAwaiter = localYieldAwaiter;
117                     builder.AwaitUnsafeOnCompleted(ref localYieldAwaiter, ref this);
118                     doFinallyBodies = false;
119                     return;
120 
121                 SecondAwaitContinuation:
122                     localYieldAwaiter = yieldAwaiter;
123                     yieldAwaiter = default(YieldAwaitable.YieldAwaiter);
124                     state = -1;
125                     SecondAwaitCompletion:
126                     localYieldAwaiter.GetResult();
127                     localYieldAwaiter = default(YieldAwaitable.YieldAwaiter);
128                     result = total;
129                 }
130                 catch (Exception ex)
131                 {
132                     state = -2;
133                     builder.SetException(ex);
134                     return;
135                 }
136             Done:
137                 state = -2;
138                 builder.SetResult(result);
139             }
140 
141             [DebuggerHidden]
142             void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine machine)
143             {
144                 builder.SetStateMachine(machine);
145             }
146         }
147     }

15.5.2 【Task實現細節】骨架方法的結構