C# 编译器是否执行;“回报值优化”;关于异步方法链

C# 编译器是否执行;“回报值优化”;关于异步方法链,c#,optimization,async-await,C#,Optimization,Async Await,不是传统意义上的返回值优化,但我想知道什么时候会出现这样的情况: private async Task Method1() { await Method2(); } private async Task Method2() { await Method3(); } private async Task Method3() { //do something async } 这显然可以写得更好: private Task Method1() { return M

不是传统意义上的返回值优化,但我想知道什么时候会出现这样的情况:

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()
生成状态机?

为什么你会问一个简单测试就能在一分钟内回答的问题

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#编译器上进行。如果您不需要等待,请不要使用它-这是您的责任

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

static Task Method()
{
  return Method("Default");
}

static async Task Method(string someString)
{
  await SomeThingAsync(someString);
}
它仍然是异步的,就像在无参数方法中执行另一个wait一样,但它避免了无用的状态机

async
关键字的唯一用途是允许您在给定方法中使用
wait
关键字。您仍然可以
等待一个非
async
的方法-要求返回
任务
,没有
async
关键字

使用与前面相同的示例,
wait
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");
    }
}
不,C#编译器不会优化它,也不应该优化它。这在概念上是两个不同的东西,这里是一个例子

在我看来,主要的区别在于异常如何传播到
Method1
Method2
的调用方。我演示了这种行为

在第一种情况下(没有状态机),将立即在调用方的堆栈帧上引发异常。如果未受影响,应用程序可能会立即崩溃(除非在同一堆栈帧上的调用链中存在另一个
async
方法)


在第二种情况下(使用状态机),返回给调用方的
Task
对象中的异常将保持休眠状态,直到稍后通过
Wait Task
Task.Wait()
观察到异常。它可能在完全不同的堆栈帧上被观察到,也可能根本没有被观察到。我发布了更多关于此的详细信息。

我想不会,因为您仍然可以从其他方法调用这些方法places@knittl对不起,我不知道你的意思。方法1和2仍然返回可以等待的任务,为什么不试试呢?编译代码并检查reflector或ildasm中的程序集。因为wait是一个C#编译器特性(而不是JIT编译器特性),所以您可以很容易地看出两者之间是否有区别。@cremor:基本上是因为我从未花时间学习CIL。是的,我很懒惰,我同意,这可能是一个很好的学习练习。我使用,一个免费的很棒的工具。重新创建
async/await
语句非常聪明,但也可以看到编译器生成的原始状态机是什么样子的。这是我没有考虑的一个要点。任何标记为async的方法都可以有效地封送任何异常到返回的任务。谢谢