• 15.5.3 【Task实现细节】状态机的结构


      状态机的整体结构非常简单。它总是使用显式接口实现,以实现.NET 4.5引入的 IAsync StateMachine 接口,并且只包含该接口声明的两个方法,即 MoveNext 和 SetStateMachine 。

       此外,它还拥有大量私有或公共字段。 状态机的声明在折叠后如代码清单15-11所示:

     1         [CompilerGenerated]
     2         private struct DemoStateMachine : IAsyncStateMachine
     3         {
     4             // Fields for parameters
     5             public IEnumerable<char> text;
     6 
     7             // Fields for local variables
     8             public IEnumerator<char> iterator;
     9             public char ch;
    10             public int total;
    11             public int unicode;
    12 
    13             // Fields for awaiters
    14             private TaskAwaiter taskAwaiter;
    15             private YieldAwaitable.YieldAwaiter yieldAwaiter;
    16 
    17             // Common infrastructure
    18             public int state;
    19             public AsyncTaskMethodBuilder<int> builder;
    20             private object stack;
    21 
    22             void IAsyncStateMachine.MoveNext()
    23         {..............}
    24      }

      在这段代码中,我将字段分割为不同的部分。我们已经知道表示原始参数的 text 字段 是由 骨架方法设置的,而 builder 和 state 字段亦是如此,三者皆是所有状态机共享的通用基础设施。

      由于需在多次调用 MoveNext() 方法时保存变量的值,因此每个局部变量也同样拥有着自己 的字段 。有时局部变量只在两个特殊的 await 表达式之间使用,而无须保存在字段中,但就我 的经验来说,当前实现总是会将它们提升为字段。此外,这么做还可以改善调试体验,即使没有 代码再使用它们,也无须担心局部变量丢值了。

       异步方法中使用的 awaiter 如果是值类型,则每个类型都会有一个字段与之对应,而如果是 引用类型(编译时的类型),则所有 awaiter 共享一个字段。本例有两个 await 表达式,分别使 用两个不同的 awaiter 结构类型,因此有两个字段 。如果第二个 await 表达式也使用了一个 TaskAwaiter ,或者如果 TaskAwiater 和 YieldAwiter 都是类,则只会有一个字段。由于一次 只能存活一个 awaiter ,因此即使一次只能存储一个值也没关系。我们需要在多个 await 表达式 之间传播awaiter,这样就可以在操作完成时得到结果。   有关通用的基础设施字段 ,我们已经了解了其中的 state 和 builder 。 state 用于跟踪踪 迹,这样后续操作可回到代码中正确的位置。 builder 具有很多功能,包括创建骨架方法返回的 Task 和 Task<T> ,即异步方法结束时传播的任务,其内包含有正确结果。 stack 字段略微有点晦 涩。当 await 表达式作为语句的一部分出现,并需要跟踪一些额外的状态,而这些状态又没有表 示为普通的局部变量时,才会用到 stack 字段。15.5.6节将介绍一个相关示例,该示例不会用于 代码清单15-11生成的状态机中。

       编译器的所有魔法都体现在 MoveNext() 方法中,但在介绍它之前,我们先来快速浏览一下 SetStateMachine 。在每个状态机中,它都具有完全相同的实现,如下所示:

    1             [DebuggerHidden]
    2             void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine machine)
    3             {
    4                 builder.SetStateMachine(machine);
    5             }

      简单来说,该方法的作用是:在builder内部,让一个已装箱状态机的复本保留有对自身的引 用。我不想深入介绍如何管理所有的装箱,你只需了解状态机可在必要时得到装箱,同时,异步 机制的各方面可保证在装箱后,还会一直使用这个已装箱的复本。这非常重要,因为我们使用的 是可变值类型(不寒而栗!)。如果允许对状态机的不同复本进行不同的修改,那么整个程序很快 就会崩溃。

      换一个角度来说(如果你开始认真思考状态机的实例变量是如何传播的,这就会变得很重 要),状态机之所以设计为 struct ,就是为了避免早期不必要的堆分配,但大多数代码都将其视 作一个类。围绕 SetStateMachine 的那些引用,让这一切正常运作。

    所有代码如下:

      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     }
    View Code
  • 相关阅读:
    shell 知识点
    folder,source folder,package 区别
    meta viewport 理解
    一张图看懂JavaScript中数组的迭代方法:forEach、map、filter、reduce、every、some
    java 报错及解决
    Charles :mac上的手机代理
    关联本地文件夹到github项目
    tomcat 安装
    Refused to display '[url]' in a frame because it set 'X-Frame-Options' to 'Deny'.
    linux 知识点
  • 原文地址:https://www.cnblogs.com/kikyoqiang/p/10128147.html
Copyright © 2020-2023  润新知