DEV Community

Async / Await: From Zero to Hero

Zhi Yuan on January 05, 2020

Async / Await: From Zero to Hero I had absolutely no idea what async / await was and learning it was hard as: There's 27 minutes wort...
Collapse
 
gabbersepp profile image
Josef Biehler • Edited

Nice post!

I wanted to add for those who are not that familiar with tasks, that using ConfigureAwait(false) is not only about performance issues but also about avoiding deadlocks in UI and Asp.Net applications.

In .NET you always should use "ConfigureAwait(false)" if you publish a library.

Second addition:
Using .Result within the UI Mainthread or within Asp.Net Request threads produces deadlocks. This also can be avoided using ConfigureAwait(false). But care attention if you use dependency injection. The scope may not flow with the code if you use ConfigureAwait(false).

And:
async/await introduces more complexity at CIL level and thus costs memory and CPU time. This should be taken into consideration when writing async code.

When do you decide to use async code?

Collapse
 
zhiyuanamos profile image
Zhi Yuan • Edited

Hey there, appreciate your helpful insights concerning ConfigureAwait(false). :)

async/await introduces more complexity at CIL level and thus costs memory and CPU time. This should be taken into consideration when writing async code.

You are spot-on concerning this. For the benefit of those reading this comment, this SO post gives 2 examples concerning the added complexity at CIL level when using async / await.

When do you decide to use async code?

I'm not sure if I got your question right, but I suppose you are asking this in relation to the added complexity at CIL level. If so, Stephen Cleary's post provides a thorough explanation on this matter, and I'll summarise some of the key points here:

It’s more efficient to elide async and await

Which you have mentioned rightly. However,

it’s important to point out that each of these gains are absolutely minimal... In almost every scenario, eliding async and await doesn’t make any difference to the running time of your application.

I suggest following these guidelines:

  1. Do not elide by default. Use the async and await for natural, easy-to-read code.
  2. Do consider eliding when the method is just a passthrough or overload.

Stephen Cleary also provides examples of pitfalls, one concerning using the using statement, and the other concerning exceptions. He gives an amazing and concise explanation there, so I'll not copy the quotes here.

Thanks for your question! You helped me to dig deeper into this topic, which resulted in me finding Stephen's blog post.

Collapse
 
gabbersepp profile image
Josef Biehler

Thanks for linking that blog posts. I will read through it. Sounds very interesting!

Collapse
 
mateiadrielrafael profile image
Matei Adriel

But what does ConfigureAwait(false) do exactly?

Collapse
 
cellivar profile image
Cellivar

There isn't an easy answer to this question that doesn't require more explanation. There is an FAQ of sorts here: devblogs.microsoft.com/dotnet/conf...

The very short and condensed version is that it signals to the async system that you want your asynchronous code to not be marshaled back to the original calling context. I highly recommend careful study of the FAQ post for more details on the consequences of that.

Thread Thread
 
mateiadrielrafael profile image
Matei Adriel

I don't use c# outside on my rare unity doodles. In case anyone is familisr with f#, is there anything similar in f# (for the async computational expressiom)?

Collapse
 
danstur profile image
danstur

"The performance of .NET and UI applications can be improved by using ConfigureAwait(false)."

Uuhhh. That's a way more complicated topic than you make it out to be. In a post that spends no time whatsoever talking about synchronization contexts giving such an advice is very illadvised.

Collapse
 
zhiyuanamos profile image
Zhi Yuan • Edited

Hey there, thanks for pointing out how my statement may be misread! Yes, I'm aware of the complexities involved so I cited Stephen Cleary's answer from SO.

What I really wanted to say is that: You can, but that doesn't mean therefore that you should do it.

Meanwhile, I've removed this statement. Does this convey the message clearer?

The performance of .NET and UI applications can be improved by using ConfigureAwait(false). Things aren't as straightforward, however. Do take a look at Stephen Cleary's answer before doing so.

Edit: I'll include Cellivar's link as well.

Collapse
 
bellonedavide profile image
Davide Bellone

Nice post! I also had troubles approaching async programming, so I wrote an article to help other devs understand the basics of this topic: code4it.dev/blog/asynchronous-prog...

Collapse
 
zhiyuanamos profile image
Zhi Yuan • Edited

Hey Davide, thanks for reading! I read through your article and found out about the existence of ValueTask, thanks for writing!

A quick comment on the header How to make a sync method asynchronous: Correct me if I'm wrong, I think such a scenario is only helpful when developing Desktop applications, whereby heavy synchronous work should not be processed by the UI thread, but deferred to a background thread using Task.Run.

Web applications, however, do not have a UI thread. Consider the code samples below (the first is copied from your article)

// Suppose Thread A is processing this incoming request
var taskResult = Task.Run( () => DoSomethingSynchronous() ); // Thread B is assigned to process DoSomethingSynchronous()
int value = await taskResult; // Thread A is freed up to process other incoming requests

VS

DoSomethingSynchronous(); // processed by Thread A

In both scenarios, a thread is required to perform DoSomethingSynchronous(); there's no benefit in doing it in an asynchronous manner. Rather, the asynchronous code would be slightly less performant as it has to deal with the overhead of async/await and allocating Thread B to execute DoSomethingSynchronous() and freeing Thread A.

Edit: I think it's beneficial to run synchronous methods on separate threads using Task.Run when executing multiple expensive synchronous methods. For example:

SomeExpensiveSynchronousMethodOne();
SomeExpensiveSynchronousMethodTwo();

VS

var taskOne = Task.Run(() => SomeExpensiveSynchronousMethodOne());
var taskTwo = Task.Run(() => SomeExpensiveSynchronousMethodTwo());
await Task.WhenAll(taskOne, tasktwo);

In the first option, the methods run sequentially, while the second option has both methods running in parallel, thus the second option should complete earlier than the first option.

Collapse
 
nbull92 profile image
NBull92

Great read and well put together. Thanks for doing this, I would have personaly made the mistake with the make waffle and coffee, then snap a picture, example you gave.

Collapse
 
zhiyuanamos profile image
Zhi Yuan

Thank you for your kind encouragement, I'm glad that this post helped you :)

Collapse
 
galwaycoder profile image
Eamon Keane

Excellent post. Really well explained.

Collapse
 
zhiyuanamos profile image
Zhi Yuan

Thanks Eamon for reading through this article and for your kind words! :)

Collapse
 
flaxven profile image
VenFlaxSankari

Nice and clear explanation Zhi Yuan.

Collapse
 
zhiyuanamos profile image
Zhi Yuan

Thank you for reading through this article and for your kind words! :)

Collapse
 
iamimrankhan95 profile image
IMRAN AHMED KHAN

Nice post

Collapse
 
zhiyuanamos profile image
Zhi Yuan

Thank you for your kind encouragement! :)

Collapse
 
johnziss9 profile image
John

Thanks for this Zhi, it was a great read, cleared things up quite a bit. I will look into it more for further knowledge.

Collapse
 
zhiyuanamos profile image
Zhi Yuan

Hey John, I'm glad that you found this short post helpful! :)