How Do I Think About Async Code?!
Increasingly essential to writing responsive applications, asynchronous code is becoming more and more popular. Unfortunately, asynchronous programming adds an additional level of complexity to your code. As a result, understanding how this code even works can be tough no matter your experience level. Whether you’re new to it or you just want a refresher, here is an intro to the world of asynchronous code!
What is asynchronous code?
Asynchronous (async) programming lets you execute a block of code without stopping (or blocking) the entire thread where the action is being executed. A common myth about async code is that it improves performance, which isn’t necessarily true. Instead, the major perk of async programming is that it increases the number of tasks (throughput) that can be executed concurrently without having to block the thread where these actions are taking place.
You may think async code seems a little like multithreaded code. After all, many methods could be executing at the same time in both. In reality, async programming can be used together with singular or multithreaded applications. This means you can have a single-threaded async program, where one thread can run concurrent tasks. Conversely, you can also have a multi-threaded async application, where multiple threads can each run multiple concurrent tasks.
Why should I use async code? Example please!
To use an analogy to demonstrate async programming, consider the action of baking a cake. This action will be represented by a thread that is executing several steps (or tasks), as illustrated in code below. This code is serviceable, and you will still have a yummy cake once the method is done executing. However, since all the code is synchronous, each line will run sequentially. In other words, you will be standing completely still while waiting for the oven to finish preheating. You could be making the batter for your cake in the meantime!
In real life, you typically want to multitask by making the batter while the oven preheats or making the icing while the cake is baking. Doing this increases your productivity and allows you to bake the cake much faster. This is where asynchronous code comes in handy! By making our existing code asynchronous, we can perform more actions to pass the time even while we are awaiting a task like baking the cake in the oven to complete. To do this, we modified our code to include a new function called PassTheTime. This code saves a task’s state, starts running another synchronous or asynchronous function, and retrieves the saved task’s value when it’s actually needed.
When compared to the synchronous MakeCake method that lacks a PassTheTime function call, MakeCakeAsync manages to complete more tasks without blocking the thread and shortens the time it takes to execute the whole method.
How do I write async code in .NET?
Thankfully, C# makes it a “piece of cake” to write asynchronous code with the Task type and the await and async keywords. The Task type tells the caller about the eventual return value type. It also indicates that other actions can execute while the caller method is being processed. The async keyword enables the await keyword, which lets the compiler know that we’ll need the return value of the function, but not right away. As a result, we don’t need to block the call and can continue running other tasks until the awaited value is needed. An async method will initially run synchronously until it hits an await keyword. This is when the asynchronous execution will begin.
I just learned about async code! Now what?
While using async code to bake a cake is great, there are plenty of other real-life applications to use asynchronous code. Two of the most common examples include:
- Programs using HTTP requests – Depending on the request, HTTP calls can take a long time to fully process. Using async code can let you perform other work while waiting for the server to respond.
- Programs using UI elements – WPF apps or any apps using buttons, textboxes, and other UX assets lend themselves well to async implementation. For instance, a WPF app taking in a file to be parsed may take a while. By making this action async, you can still interact with the UI without having the app completely freeze as you wait for the function to complete.
Now that you know the basics of asynchronous programming, it’s time to improve on it! There are a lot of secret do’s and don’ts when writing solid async code. For a great resource that explores these tips and tricks, check out David Fowler’s post on async programming.
Like all code, there will come a time where you’ll need to diagnose bugs in your async program. To learn how to debug your async code in Visual Studio, tune in for an upcoming blog post…
Side note: And no, I did not use the cake analogy just because I’ve been doing a lot of baking recently. 😉
Yes, C# does make it insanely easy to use async.
Having said that, and you’re in the VS team, can you please tell us when will the following be available:
* Speed up UWP compile times – it’s simply insane building non-trivial UWP apps. Compile times are to the roof! MS has known about this for years.
* Being able to see the async stack trace in UWP. Losing the initial context is makes it really hard to debug UWP apps.
Hi Leslie – nice article and a good “real world” example of async! I think the wording around the “PassTheTime” explanation is a little confusing (at least for me!) since it implies that “PassTheTime” is handling the asynchrony and state restoration rather than the “await” – you kind of skipped over the crucial point that the async methods run until they hit a blocking operation so by making the operations async you’re really changing the semantics to be “StartBakingTheCake” etc. (Obvious if you already know it but seems like your article is aimed at beginners to async who might take some time to figure this out.)
BTW – there is a very nice library called TaskTupleAwaiter (https://github.com/buvinghausen/TaskTupleAwaiter/blob/master/README.md) that introduces a nice syntax for this kind of “just run a few arbitrary async things in parallel”. To my eye the visual presentation it allows..
makes it more obvious that these things are being done together compared to placing them on sequential lines.
I enjoyed the article Leslie. I’ve always been curious… Most code examples on MSDN await every task immediately. Even the out-of-box new project Visual Studio templates await every task immediately. Isn’t this the same as synchronous programming? Why use asynchronous keywords in these cases? To achieve asynchronous behavior I’ve always done what you’ve done here. I assign the task to a variable, and await it later. I look forward to your comments.
When you await the task depends on when you need the result, but since the typical case is that we make the async call and want to use the result immediately it’s simpler and cleaner to just write var someResult = await DoWorkAsync();.
The real magic of async/await it that it looks like synchronous code, but what’s happening is that when you call your DoWorkAsync() method it returns a Task instead of just returning a synchronous result.
If that task isn’t Completed when execution reaches the await then, instead of the executing thread blocking as would happen in synchronous code, the thread returns to the thread pool and is free to pick up other work. When DoWorkAsync() completes a free thread will resume execution at the statement following the await.
So where you put the await doesn’t make your code synchronous. You simply await the task at the point you want the result. For tasks that don’t return results you can think of the result as being ‘the task has completed’, generally for its side effects.
This is why the async/await pattern mostly applies to IO calls where the thread that would otherwise be blocked can go and do useful work during the few milliseconds the IO takes, and that means we can service potentially thousands of IO calls with one or a few threads.
Very nice post! I need to do this more often, and your post inspires me to do so. Thanks.
there are two kinds of parallel activities: threads and tasks. Asynchronous program is a parallel program which employs tasks (and, most likely, threads). Any parallel program can be implemented in both ways. The advantage of tasks is less memory consumption, because they do not have attached stack. The disatwantage is more complex programming: the work of a single thread often requires more than one tasks. Besides, programmer must not allow their tasks to perform bloking operations.
Thanks for your concise definition describing the differences between these two paradigms.
Any ideas where can I download the code to create the app as shown in your blog? I just want to get started so the best thing is to see how this code was created.
Want to compare with parallel processing but your code looks very efficient.
Here’s a link to the GitHub repo: https://github.com/leslierichardson95/MakeCakeAsync
“An async method will initially run synchronously until it hits an await keyword.”
That sounds as if first PreheatTask is run and THEN ingredients are added and THEN preheat result is retrieved asynchronously.
In fact (I think) the opposite happens: main function and preheat function run ASYNCHRONOUSLY until AWAIT forces them to sync up again.
(I think I read somewhere that the compiler splits an async function into an initial synchronous block…but that’s irrelevant in this introduction and causes confusion)
Async code, despite all this hype around, is not the thing you can push everywhere!
First of all, not everywhere you need “quick responsibility”. If you wanna HTML page, what the heck you need clicking on half-empty page?? First wait until it’s loaded and THEN interact. Same with many operations: when you save a document, WAIT – make SURE it’s safe and then continue work. Esp. when you work with slow media. Esp. when Windows behaves like slowpoke on simplistic operations (because of dirt architecture).
Second, async code makes code more complex and harder to debug. Read: “makes code error prone”. I rather pay for RELIABLE code than “fast written”. MS thinks different and that’s why we have “cr**ppy quality” of Win10.
Stop the hype, we all already know what async is. No more push, leave us alone and let decide when WE need that feature.
Vincent, you are correct. The “follow the herd mentality” has lead coders to believe that Async coding is some sort of revolution. Even the example given above (baking cake) is technically not correct. The making pizza example given by people on other sites is also not correct. Here is why……
When you turn an oven on, and go on to do other things (say, making icing); somebody or something has to be heating the oven. Yes, the real-life-oven is an appliance, but something is going on inside that heats it – consumption of electricity, the moving fan, the heating element, etc. In true coding terms, that is a – Process – a piece of code being executed in shared or private space (a thread is also a process, with shared space across some other processes).
Again, what it means is, “heating oven” function will be executing across another process/thread, it may not be your process, but it IS a separate process. So, technically it would be incorrect to say that — Async coding can happen over one thread. If you really only had one thread, then ONLY one async function will run at any given time, just like without using Async word (the default sync code).
Because today’s CPUs often have more than one CPU (core), yes, you can run more than one process at the same time. But your OS will also time-slice CPUs, to give the impression that it is running even more processes (in reality, a single execution CPU-element can only run ONE process at any time).
I feel Async coding was added to C#, as Windows processes consume too much memory/resources to get started (compared with say Linux). So, to speed up things a little why not start a group of processes (threads), even before you need them, and then just allocate them as needed (thread pool). It is like hiring some spare employees even when you do not need them, yes you still have to pay them, and yes, thread pool will also have overhead. Constantly asking employees to do different tasks will be even more overhead. How would you feel, if you were asked to constantly drop what you were doing, and take on a new task. Constantly re-using same threads for different tasks (functions), should also have the same overhead of saving process states all the time.
In such a scenario, when you execute a function (say F1) asynchronously, and start doing something else, the function F1 will “sleep/halt” or will have to run on some other thread (from pool may be). It is not really solving any problem.
In fact, there is no real problem with normal (you may call it – Sync) coding.
Asyn coding will likely way over-complicate your coding; costing more time, money, and adding some nice bugs to your code!
Remember: The simplest solution to a problem, is almost always, the best solution. But there is no problem here that Async solves, in the real world of coding.
Am I incorrect? If yes, please provide clear reasons, thanks.
On a side note the real “logic” behind Async stuff is in electronics; it makes sense in that context (you can discover on internet, how).
Thanks for an interesting article. Good examples always improve understanding. For async, I think a better example than baking a cake is a restaurant with multiple waiters and chefs. In the synchronous solution, the waiter goes to the chef with an order. The waiter then waits in the kitchen for the food to be cooked. While he is waiting he cannot serve any other guests. In the async solution, the waiter goes to the kitchen with the order and then immediately returns to the tables to take other orders. The chef rings the bell when the food is ready and the waiter picks up the food and brings it to the guests.
Well done! This is perhaps the most succinct article/example of asynchronous coding I have read. Illustrating a simple and straightforward benefit of async/await is an extremely smart use of the reader’s time. Thank you, will be sharing this article with our other developers.
Awesome, easy to understand example. Thanks for the source code too!
Wait, are you saying just calling an async method from an async method, actually causes the last method to start running asynchronously at that point, even if you don’t actually await for it until later? That was the part of the code that was different from other examples, normally the await appears on the same line as the method call, instead of storing the return value in a Task variable. Are you sure Task is actually running before calling await on it later on? What if you don’t await for it all? If you just want it to run and finish whenever, and there is no result back that you need, is that okay?
Excellent post and an easy to understand example how Async works using the cake analogy. Best example I have come across.
I wanted to thank you for the article, but more importantly, thank you for your example code not having spaghetti returns all over the place. A single exit point is a beautiful thing. 🙂
very nicely done (baked ..multi layered colorful cake)!!!
can we introduce rating to these blogs and a search option at the top (advanced search …by author, date, technology…etc)? May be I dont know how to ??!!