zoukankan      html  css  js  c++  java
  • 15.5.4 【Task实现细节】一个入口搞定一切

      如果你反编译过异步方法(我非常希望你会这么做),会看到状态机中的 MoveNext() 方法 非常长,变化非常快,像是一个计算有多少 await 表达式的函数。它包含原始方法中的所有逻辑, 和处理所有状态变换所需要的芭蕾舞步 ① ,以及用来处理整个结果或异常的包装代码。

      在手动编写异步代码时,你通常会将后续操作分散到多个方法内:在一个方法内开始,然后 在另一个方法内继续,并且可能在第三个方法中结束。但这样很难处理循环等流控制,对C#编译 器来说更是不可能的。这和生成代码的可读性差是两码事。状态机具有单独的入口,即 MoveNext() 方法。该方法在一开始便投入使用,并且可用于所有 await 表达式的后续操作。每当调用 MoveNext() 方法时,状态机就会通过 state 字段计算出方法要跳转到的位置。在准备计算结果 时,则跳转到方法的逻辑起始位置或 await 表达式的末尾。每个状态机只执行一次操作。实际上, 在方法内部存在一个基于 state 的 switch 语句,每种情况都具有包含不同标签的对应 goto 语句。

    MoveNext() 方法一般为以下形式:

     1             void IAsyncStateMachine.MoveNext()
     2             {
     3                 //对于声明返回Task<int>的异步方法
     4                 int result = default(int);
     5                 try
     6                 {
     7                     bool doFinallyBodies = true;
     8                     switch (state)
     9                     {
    10                         //跳转到正确的位置
    11                     }
    12                     //方法主体
    13                 }
    14                 catch (Exception e)
    15                 {
    16                     state = -2;
    17                     builder.SetException(e);
    18                     return;
    19                 }
    20                 state = -2;
    21                 builder.SetResult(result);  
    22             }

      初始状态始终为 -1 ,方法执行时状态也是 -1 (与等待时被暂停相反)。非负值均表示一个后 续操作的目标。状态机在结束时状态为 -2 。在调试配置下创建的状态机中,你会看到一个指向 -3 状态的引用——此状态是我们未曾预料到的。退化的 switch 语句会导致糟糕的调试体验,而 -3 状态即是为避免该退化语句的出现而存在的。

      在方法执行过程中,在原始异步方法的 return 语句处,会设置 result 变量。然而在到达方 法的逻辑末尾时,将其用于 builder.SetResult() 的调用。即使是非泛型的 AysncTask MethodBuilder 和 AsyncVoidMethodBuilder 类型,也包含 SetResult() 方法。前者表示对 于从骨架方法返回的任务来说,该方法已经完成;后者则表示原始的 SynchronizationContext 已经完成。(异常会以同样的方式向原始的 SynchronizationContext 传播。这是一种相当丑陋 的跟踪方式,但却对必须使用 void 方法的场景提供了一种解决方案。)

      doFinallyBodies 变量用于计算执行过程离开 try 块的作用域时,原始代码中的 finally 块(包括 using 或 foreach 语句中的隐式 finally 块)是否应该执行。理论上,只有以正常方式 离开 try 块的作用域时,我们才希望执行 finally 块。如果我们只是从之前为awaiter附加了后续 操作的方法中返回,由于该方法逻辑上已经“暂停”了,因此我们不希望再执行 finally 块。 finally 块均位于相关的 try 块之后,并出现在代码方法部分的 Main 主体中。

      从原始异步方法的角度来看,大多方法体都是可识别的。当然,你需要习惯于所有局部变量 都作为状态机的实例变量,但这并不是什么难事。正如你所想的那样,棘手之处是 await 表达式。

  • 相关阅读:
    jQuery学习(三)
    HTML基础
    对于跨域问题的解决
    Spring boot 默认静态资源路径与手动配置访问路径
    json:java中前台向后台传对象数据
    javascrit常用互动方法
    java IO流
    HTML中data* 属性
    java中一些对象(po,vo,dao,pojo)等的解释
    使用mybatis generator插件,自动生成dao、dto、mapper等文件
  • 原文地址:https://www.cnblogs.com/kikyoqiang/p/10128202.html
Copyright © 2011-2022 走看看