每个"等待"运算符都会导致状态机吗?

sha*_*y__ 5 c# asp.net clr async-await

请考虑以下代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

i3a*_*non 10

await在这种情况下并不重要.每个async方法都会生成一个状态机(即使它根本没有awaits).

您可以通过此TryRoslyn示例看到它.

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

private async Task<string> D()
{
    var data = await FetchFromDB();
    return data;
}
Run Code Online (Sandbox Code Playgroud)

您可以删除async关键字及其附带的状态机:

private Task<string> D()
{
    return FetchFromDB();
}
Run Code Online (Sandbox Code Playgroud)

但是否则,你实际上需要状态机,async没有它就无法操作方法.

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


Ser*_*rvy 5

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

请记住,状态机的"开销"主要是一个对象的分配(这个和几个gotos会很快),所以你执行的任何类型的"优化"都是与不创建一次类的实例相同.

至于它的成本是否大于或小于同步工作,那么你需要根据应用程序和硬件的具体情况来确定性能基准.


Yuv*_*kov 5

每个方法都会转化为状态机吗?或者编译器是否足够复杂以生成更有效的结构?

不,编译器将为每个调用生成一个状态机。编译器不会检查方法的语义调用链。它将仅在方法的基础上生成状态机。

当查看编译后的代码时,您可以清楚地看到这一点:

编译器生成的代码:

这是否会影响异步等待开销(例如状态机)和非阻塞线程优势之间的权衡?

您必须测试您的代码才能说出这一点。一般来说,当您需要吞吐量时,异步 IO 比较好。如果您的异步方法将被多个调用者同时调用,您将能够看到好处。如果没有,您可能看不到性能提升的任何效果。再次对您的代码进行基准测试。