15.5.6 【Task实现细节】跟踪栈

  谈到栈帧(stack frame)时,可能会想到在方法中声明的局部变量。当然,可能还会注意到 一些隐藏的局部变量,如 foreach 循环中的迭代器。但栈上的内容不止这些,至少逻辑上是这样  。 很多情况下,在一些表达式还没有计算出来前,另一些中间表达式是不能使用的。最简单的例子 莫过于加法等二进制操作和方法调用了。 举个极简单的例子,思考下面这一行:

  var x = y * z;

在基于栈的伪代码中,将为如下形式:

  push y
  push z
  multiply
  store

现在假设有如下 await 表达式:

  var x = y * await z;

  在等待 z 之前,需计算 y 并将其保存至某处,但可能会从 MoveNext() 方法立即返回,因此需 要一个逻辑栈来存储 y 。在执行后续操作时,可以重新存储该值,然后执行乘法。在这种情况下, 编译器可将 y 的值赋值给 stack 实例变量。这会引起装箱,但同时也意味着可以使用单个变量。 这是个简单的例子。假设有多个值需要存储,如下所示:

  Console.WriteLine("{0} :{1}", x, await task);

在逻辑栈上需要存储格式化的字符串和 x 值。此时编译器会创建一个包含两个值的Tuple<string, int> ,并将其存储在 stack 的引用上。和 awaiter 一样,同一时间只需要一个逻辑栈,因此一直使用相同的变量是没有问题的。在后续操作中,可以从元组(tuple)中获取
  实参,并用于方法调用。可下载的源代码中包含了完整的反编译示例,其中包括以上两条语句( LogicalStack.cs 和 LogicalStackDecompiled.cs )。

  第二条语句最终将使用以下代码:

 1                     string localArg0 = "{0} {1}";
 2                     int localArg1 = x;
 3                     localAwaiter = task.GetAwaiter();
 4                     if (localAwaiter.IsCompleted)
 5                     {
 6                         goto SecondAwaitCompletion;
 7                     }
 8                     var localTuple = new Tuple<string, int>(localArg0, localArg1);
 9                     stack = localTuple;
10                     state = 1;
11                     awaiter = localAwaiter;
12                     builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
13                     doFinallyBodies = false;
14                     return;
15                 SecondAwaitContinuation:
16                     localTuple = (Tuple<string, int>) stack;
17                     localArg0 = localTuple.Item1;
18                     localArg1 = localTuple.Item2;
19                     stack = null;
20                     localAwaiter = awaiter;
21                     awaiter = default(TaskAwaiter<int>);
22                     state = -1;
23                 SecondAwaitCompletion:
24                     int localArg2 = localAwaiter.GetResult();
25                     Console.WriteLine(localArg0, localArg1, localArg2);

此处加粗显示的是与逻辑栈元素相关的代码。 目前所有需了解的内容均已介绍完毕。如果你跟上了我们的步伐,就肯定比99%的开发者更 为了解背后的细节。第一次没能完全理解也没有关系。在阅读这些状态机代码时,如果感到无法 理清头绪,可以暂时放松一下,过一会儿再来继续学习。

原文地址:https://www.cnblogs.com/kikyoqiang/p/10128305.html