编译器是否对异步方法链执行“返回值优化”

在传统意义上没有返回值优化,但我想知道你什么时候有这样的情况:

private async Task Method1() { await Method2(); } private async Task Method2() { await Method3(); } private async Task Method3() { //do something async } 

这显然可以写得更优:

 private Task Method1() { return Method2(); } private Task Method2() { return Method3(); } private async Task Method3() { //do something async } 

我只是想知道是否有人知道(MS)编译器是否足够聪明,不能在第一个实例中为Method1()Method1()生成状态机?

不,C#编译器不优化它,它不应该 。 这些在概念上是两个不同的东西,这是一个类似的问题 。

IMO,主要区别在于exception如何被传播到Method1Method1的调用者。 我在这里演示了这个行为。

在第一种情况下(没有状态机),将立即在调用者的堆栈帧上抛出exception。 如果它是unhanded,应用程序可能会立即崩溃(除非在同一堆栈帧的调用链中有另一个async方法)。

在第二种情况下(使用状态机),exception将在返回给调用者的Task对象中保持hibernate状态 ,直到通过await tasktask.Wait()观察到一段时间之后。 它可能在完全不同的堆栈帧上被观察到,或者可能根本没有被观察到。 我在这里发布了一些关于此的更多细节。

你为什么要通过简单测试就能在一分钟内提出一个问题?

 class Program { static void Main(string[] args) { MainAsync().Wait(); Console.ReadLine(); } static async Task MainAsync() { await Method1(); } static async Task Method1() { await Method2(); } static async Task Method2() { await Method3(); } static async Task Method3() { Console.Write("Start"); await Task.Delay(1000); Console.Write("End"); } } 

这在IL中创建了四个不同的状态机。

IL代码必须是这种方式,因为您可以从任何地方调用方法,并且它们必须一致地运行,因此任何优化都必须在JIT级别上完成,而不是C#编译器。 如果您不需要await ,请不要使用它 – 这是您的责任。

一个很好的例子是方法重载:

 static Task Method() { return Method("Default"); } static async Task Method(string someString) { await SomeThingAsync(someString); } 

它仍然像在无参数方法中做另一个等待一样异步 – 但它避免了无用的状态机。

async关键字的唯一目的是允许您给定方法中使用await关键字。 您仍然可以awaitasync的方法 – 要求是返回Task ,而不是async关键字。

使用与以前相同的示例, await s是多余的。 一个更简单的方法是:

 class Program { static void Main(string[] args) { MainAsync().Wait(); Console.ReadLine(); } static async Task MainAsync() { await Method1(); await Method2(); } static Task Method1() { return Method2(); } static Task Method2() { return Method3(); } static async Task Method3() { Console.Write("Start"); await Task.Delay(1000); Console.Write("End"); } }