我正在阅读C# in depth中的异步模式,我决定采用Jon的建议并对异步代码进行反编译,以实际查看发生了什么。我遇到了两个让我感到困惑的场景,为什么它们不同。
考虑这两个功能:
public static async Task<string> GetValue(){
int count = 0;
count++;
string g = "asdf";
var r = Task.FromResult<string> ("hello");
return g;
}
并且
public static async Task<string> GetValue(){
int count = 0;
count++;
string g = "asdf";
var r = await Task.FromResult<string> ("hello");
return g;
}
现在我希望代码输出相同的IL,因为它们都是异步函数,所以需要一个状态机。但令我惊讶的是,C#编译器确实在两种情况下创建状态机,遵循所有相同的代码,除了在第一个代码块中它没有实际保存机器中的任何信息。在第二个它存储所有变量的位置。
编译器是否有理由决定不在状态机中保存变量并根据await
关键字公开两个不同的代码路径?
答案 0 :(得分:2)
第一个应该给你一个关于不是真正异步的警告(&#34; ...将同步运行&#34;),因为那里没有await
。
因此,有一个状态机,因为您将其标记为async
,并且可以使用await
进行调用。
但是没有信息要保留,因为方法中没有await
可以使用它。
答案 1 :(得分:1)
我认为这种差异源于缺乏await
运营商。如果没有await
,编译器会将您的代码视为同步代码。没有理由使用state machine
。
因此,本质上它会创建state machine
,因为它需要asynchronous
代码,但实际上并没有实现async
的任何代码。这使它成为一个空容器。
设置代码初始化用于表示的状态机 异步方法然后使用调用将其关闭 状态机上的辅助MoveNext方法。这个状态机 type保存异步方法的状态,允许该状态 如有必要,可以在异步等待点中持久保存。
由于await
,状态机会在第二个示例中保存您的数据。这会触发状态机通过多个await points
持久保存数据。由于另一个例子不包含等待,它仍然是空的。它只创建了状态机,因为你把它装饰成async
,尽管它确实不是。
关于此事的article的一小段摘录,希望这会有所帮助。
答案 2 :(得分:1)
只需要捕获await
表达式可见的变量:
public static async Task Method() {
int a=3;
Console.WriteLine(a);
{
int b=3;
Console.WriteLine(b);
}
await Task.FromResult(true);
}
例如,在捕获的a
上方的代码中,未捕获b
。因此,如果您不使用await
,则无需捕获任何内容。