Will every 'await' operator result in a st

2019-01-26 16:11发布

问题:

Please consider the following code:

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;
}

This code demonstrate a most basic functionality - nested async methods. Will every method get translate into a state machine? Or is the compiler sophisticated enough to generate a more efficient structure? In some of my projects, there are ~20 methods between the UI/WebAPI and the I/O call - does that affect the trade-off between the async-await overhead (such as the state machine) and the non-blocking thread benefits? I mean, if, for example, the overhead of 4 state machines (4 nested async methods) equals to 50ms of blocking I/O (in terms of trade-off), will 20 state machine be equal to longer I/O's delay (250ms)?

回答1:

Every method is going to have a state machine, yes.

Keep in mind that the "overhead" of the state machine is primarily the allocation of one object (that and a few gotos, which are going to be quite fast), so any type of "optimization" that you perform to remove it is the same as not creating an instance of a class once.

As to whether or not it's cost is greater or less than doing the work synchronously, that's something you're going to need to do performance benchmarks on given the specifics of your application and hardware to know for sure.



回答2:

await doesn't matter in this case. Each async method will generate a state machine (even if it has no awaits at all).

You can see that with this TryRoslyn example.

If you have cases where a state machine isn't needed, where the method doesn't really need to be async like this one for example:

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

You can remove the async keyword and the state machine that comes with it:

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

But otherwise, you actually need the state machine and an async method can't operation without it.

You should note that the overhead is quite small and is usually negligible compared to the resources saved by using async-await. If you realize that's not the case (by testing) you should probably just make that operation synchronous.



回答3:

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

No, the compiler will generate a state-machine for each of these calls. The compiler doesn't check the semantical call-chain of your methods. It will generate a state-machine on a method basis only.

You can see that clearly when looking at the compiled code:

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

You will have to test your code in order to be able to say that. Generally, async IO is good when you need through-put. If your async methods are going to be hit concurrently by multiple callers, you'll be able to see the benefits. If not, you might not see any effect of a performance gain. Again, benchmark your code.