Home > Mobile >  Should async method always call await, if not what is the implication?
Should async method always call await, if not what is the implication?

Time:03-05

In this example,

private async void Button1_Click(object sender, EventArgs e)
{
    if (condition) await Task.Run(Foo);
}

private void Foo()
{
    Thread.Sleep(5000);
}

sometimes condition is false, and the async method awaits nothing. But consider this example,

private async void Button1_Click(object sender, EventArgs e)
{
    await Task.Run(Foo);
}

private void Foo()
{
    if (condition) Thread.Sleep(5000);
}

where the method is always awaited, even if condition is false. I'm wondering what happens behind the scenes, if one is more preferable to the other, I mean objectively if there are compiler optimizations which make one preferable over the other. Assume condition can be checked from any thread, and has a very minimal impact to performance.

It seems that when the condition check is deferred, there is always a task being awaited, but when it's false in the handler, I have a situation which is close to one where the async method lacks an await operator, about which the compiler warns me. So it feels both wrong and right at the same time. Hoping someone can shed some objective light on this question.

CodePudding user response:

Should async method always call await, if not what is the implication?

As I describe on my blog, async methods always begin executing synchronously, just like any other methods. await is where things may get asynchronous.

If a code path is taken where there is no await (or if the tasks awaited are already completed), then the method completes synchronously and returns an already-completed task to its caller. This is not a problem in practice, because "*Async" means "may be asynchronous", not "must be asynchronous".

In this specific example, you're using async void, but if this was an async Task method that was called a lot, in that case I'd recommend considering returning ValueTask instead, which would save some memory allocation for the Task whenever it completes synchronously.

CodePudding user response:

The async keyword on the method is nearly an indicator to the compiler that the code inside the method should be converted into a state-machine with state transitions at the awaits.

The only technical down-side, that I am aware of, to having an async method that doesn't await anything, is the small amount of overhead introduced by the state-machine. It should have no negative effects, other than a very small performance impact, and a tiny bit of memory-pressure, that can most likely be ignored in your scenario.

CodePudding user response:

In your particular case Thread.Sleep(5000) is not the best way of pausing the execution. Thread.Sleep(5000) is an old API and it blocks the execution thread for a given time (5000ms). What does that mean? It means the thread won't be available for other tasks. Let's say, given your computer CPU output, you have 4 threads in total and you lock 1 of them for 5000ms - it is really bad especially if it is a web application that has to handle concurrent API requests. What to use instead? await Task.Delay(5000) it will "hang" the execution for a given time, yet the thread is going to come back to the thread pool and will be available for other tasks.

Now closer to your question. Wrapping non-asynchronous code into Task and awaiting won't do anything. Async/await was designed for I/O operations specifically for not blocking threads that are waiting for I/O to complete. Wrapping code into Task and not awaiting - is basically "fire and forget" which means your execution thread may continue to execute the next block without waiting for your Task method to complete. Why I say may is because if your code wrapper in Task is fast enough it would work synchronously

  • Related