在传统意义上没有返回价值优化,但我想知道你什么时候出现这样的情况:
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()
和Method2()
生成状态机?
答案 0 :(得分:8)
不,C#编译器没有优化它,不应该。这些在概念上是两个不同的东西,这里是similar question。
IMO,主要区别在于如何将异常传播到Method1
和Method2
的来电者。我演示了这个行为here。
在第一种情况下(没有状态机),在调用者的堆栈帧上会立即抛出异常。如果它是unhanded,应用程序可能会立即崩溃(除非在同一堆栈帧的调用链中有另一个async
方法)。
在第二种情况下(使用状态机),异常将在返回给调用者的Task
对象中保持休眠,直到通过await task
或一段时间之后task.Wait()
。它可能会在完全不同的堆栈帧上观察到,或者根本无法观察到。我发布了有关此here的更多详细信息。
答案 1 :(得分:2)
为什么你只需要测试就可以在一分钟内回答一个问题?
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
关键字 。您仍然可以await
一个不是async
的方法 - 要求返回Task
,而不是async
关键字。
使用与以前相同的示例,await
是多余的。一个更简单的方法是:
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");
}
}