每个 'await' 运算符都会产生一个状态机吗?

Will every 'await' operator result in a state machine?

请考虑以下代码:

public async Task<string> GetString()
{
    //Some code here...
    var data = await A();
    //Some more code...
    return data;
}
private async Task<string> A()
{
    //Some code here..
    var data = await B();
    //manipulating data...
    return data;
}
private async Task<string> B()
{
    //Some code here..
    var data = await C();
    //manipulating data...
    return data;
}
private async Task<string> C()
{
    //Some code here..
    var data = await FetchFromDB();
    //manipulating data...
    return data;
}
private async Task<string> FetchFromDB()
{
    return await SOME_HTTP_REQUEST;
}

这段代码演示了一个最基本的功能——嵌套异步方法。 每种方法都会转化为状态机吗?或者编译器是否足够复杂以生成更高效的结构? 在我的一些项目中,在 UI/WebAPI 和 I/O 调用之间有大约 20 种方法——这是否会影响异步等待开销(例如状态机)和非同步等待开销之间的权衡? -阻塞线程的好处? 我的意思是,例如,如果 4 个状态机(4 个嵌套异步方法)的开销等于 50 毫秒的阻塞 I/O(就权衡而言),那么 20 个状态机是否等于更长 I/O 的延迟(250 毫秒)?

await 在这种情况下无关紧要。 每个async方法都会生成一个状态机(即使它根本没有await)。

你可以通过 this TryRoslyn example 看到这一点。

如果您遇到不需要状态机的情况,那么该方法实际上并不需要 async,例如:

private async Task<string> D()
{
    var data = await FetchFromDB();
    return data;
}

可以去掉async关键字及其自带的状态机:

private Task<string> D()
{
    return FetchFromDB();
}

但除此之外,您实际上需要状态机,async 方法没有它就无法运行。

您应该注意到,开销非常小,与使用 async-await 节省的资源相比通常可以忽略不计。如果您意识到情况并非如此(通过测试),您可能应该使该操作同步。

每个方法都会有一个状态机,是的。

请记住,状态机的 "overhead" 主要是分配一个对象(那个和一些 gotos,这将非常快),所以任何类型"optimization" 执行删除操作与不创建 class 实例相同。

至于它的成本是否高于或低于同步执行工作,这是您需要根据应用程序和硬件的具体情况进行性能基准测试才能确定的事情。

Will every method get translate into a state machine? Or is the compiler sophisticated enough to generate a more efficient structure?

不,编译器将为这些调用中的每一个生成一个状态机。编译器不会检查您的方法的语义调用链。它将仅基于方法生成状态机。

看编译后的代码就清楚了:

does that affect the trade-off between the async-await overhead (such as the state machine) and the non-blocking thread benefits?

您必须测试您的代码才能这样说。通常,当您需要吞吐量时,异步 IO 很好。如果您的异步方法将被多个调用者同时调用,您将能够看到好处。否则,您可能看不到性能提升的任何影响。再次对您的代码进行基准测试。