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

 

转载于:https://www.cnblogs.com/kikyoqiang/p/10128147.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值