If async-await doesn't create any additional threads, then how does it make applications responsive?
Actually, async/await is not that magical. The full topic is quite broad but for a quick yet complete enough answer to your question I think we can manage.
Let's tackle a simple button click event in a Windows Forms application:
public async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before awaiting");
await GetSomethingAsync();
Console.WriteLine("after awaiting");
}
I'm going to explicitly not talk about whatever it is GetSomethingAsync
is returning for now. Let's just say this is something that will complete after, say, 2 seconds.
In a traditional, non-asynchronous, world, your button click event handler would look something like this:
public void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before waiting");
DoSomethingThatTakes2Seconds();
Console.WriteLine("after waiting");
}
When you click the button in the form, the application will appear to freeze for around 2 seconds, while we wait for this method to complete. What happens is that the "message pump", basically a loop, is blocked.
This loop continuously asks windows "Has anyone done something, like moved the mouse, clicked on something? Do I need to repaint something? If so, tell me!" and then processes that "something". This loop got a message that the user clicked on "button1" (or the equivalent type of message from Windows), and ended up calling our button1_Click
method above. Until this method returns, this loop is now stuck waiting. This takes 2 seconds and during this, no messages are being processed.
Most things that deal with windows are done using messages, which means that if the message loop stops pumping messages, even for just a second, it is quickly noticeable by the user. For instance, if you move notepad or any other program on top of your own program, and then away again, a flurry of paint messages are sent to your program indicating which region of the window that now suddenly became visible again. If the message loop that processes these messages is waiting for something, blocked, then no painting is done.
So, if in the first example, async/await
doesn't create new threads, how does it do it?
Well, what happens is that your method is split into two. This is one of those broad topic type of things so I won't go into too much detail but suffice to say the method is split into these two things:
- All the code leading up to
await
, including the call toGetSomethingAsync
- All the code following
await
Illustration:
code... code... code... await X(); ... code... code... code...
Rearranged:
code... code... code... var x = X(); await X; code... code... code...
^ ^ ^ ^
+---- portion 1 -------------------+ +---- portion 2 ------+
Basically the method executes like this:
It executes everything up to
await
It calls the
GetSomethingAsync
method, which does its thing, and returns something that will complete 2 seconds in the futureSo far we're still inside the original call to button1_Click, happening on the main thread, called from the message loop. If the code leading up to
await
takes a lot of time, the UI will still freeze. In our example, not so muchWhat the
await
keyword, together with some clever compiler magic, does is that it basically something like "Ok, you know what, I'm going to simply return from the button click event handler here. When you (as in, the thing we're waiting for) get around to completing, let me know because I still have some code left to execute".Actually it will let the SynchronizationContext class know that it is done, which, depending on the actual synchronization context that is in play right now, will queue up for execution. The context class used in a Windows Forms program will queue it using the queue that the message loop is pumping.
So it returns back to the message loop, which is now free to continue pumping messages, like moving the window, resizing it, or clicking other buttons.
For the user, the UI is now responsive again, processing other button clicks, resizing and most importantly, redrawing, so it doesn't appear to freeze.
2 seconds later, the thing we're waiting for completes and what happens now is that it (well, the synchronization context) places a message into the queue that the message loop is looking at, saying "Hey, I got some more code for you to execute", and this code is all the code after the await.
When the message loop gets to that message, it will basically "re-enter" that method where it left off, just after
await
and continue executing the rest of the method. Note that this code is again called from the message loop so if this code happens to do something lengthy without usingasync/await
properly, it will again block the message loop
There are many moving parts under the hood here so here are some links to more information, I was going to say "should you need it", but this topic is quite broad and it is fairly important to know some of those moving parts. Invariably you're going to understand that async/await is still a leaky concept. Some of the underlying limitations and problems still leak up into the surrounding code, and if they don't, you usually end up having to debug an application that breaks randomly for seemingly no good reason.
- Asynchronous Programming with Async and Await (C# and Visual Basic)
- SynchronizationContext Class
- Stephen Cleary - There is no thread well worth a read!
- Channel 9 - Mads Torgersen: Inside C# Async well worth a watch!
OK, so what if GetSomethingAsync
spins up a thread that will complete in 2 seconds? Yes, then obviously there is a new thread in play. This thread, however, is not because of the async-ness of this method, it is because the programmer of this method chose a thread to implement asynchronous code. Almost all asynchronous I/O don't use a thread, they use different things. async/await
by themselves do not spin up new threads but obviously the "things we wait for" may be implemented using threads.
There are many things in .NET that do not necessarily spin up a thread on their own but are still asynchronous:
- Web requests (and many other network related things that takes time)
- Asynchronous file reading and writing
- and many more, a good sign is if the class/interface in question has methods named
SomethingSomethingAsync
orBeginSomething
andEndSomething
and there's anIAsyncResult
involved.
Usually these things do not use a thread under the hood.
OK, so you want some of that "broad topic stuff"?
Well, let's ask Try Roslyn about our button click:
Try Roslyn
I'm not going to link in the full generated class here but it's pretty gory stuff.
Does the use of async/await create a new thread?
In short NO
From Asynchronous Programming with Async and Await : Threads
The async and await keywords don't cause additional threads to be
created. Async methods don't require multithreading because an async
method doesn't run on its own thread. The method runs on the current
synchronization context and uses time on the thread only when the
method is active. You can use Task.Run to move CPU-bound work to a
background thread, but a background thread doesn't help with a process
that's just waiting for results to become available.
If async/await doesn't create new thread then explain this code
You're using a console application for your example. This effects greatly the outcome of your test. A console application has no custom SynchronizationContext
(like Winforms, WPF and ASP.NET have), hence it uses the ThreadPoolTaskScheduler
to schedule continuations on an arbitrary thread-pool thread. Try this same example in a UI application and you'll see the continuation invoked on the same thread.
Keeping a responsive UI while using async/await and multithreading
It's unclear what your code actually does but just because a method has a asynchronous API doesn't necessarily means that it is implemented to not block.
Consider the following method as an example. It appears to be async from a callers perspective but it clearly isn't:
public Task<int> MyNotSoAsyncMethod()
{
//I'm actually blocking...
Thread.Sleep(3000);
return Task.FromResult(0);
}
How can I keep the UI thread from getting blocked?
If you want to be sure not to block the calling thread regardless of the implementation of the method you are calling, then call it on a background thread. For example by creating a Task
:
await Task.Run(DoSomethingThatMightBlock);
Does await create another thread internally before shifting to the same thread as the caller (for UI application)
But my question is, does it create a new thread (internally) after IO is complete and before moving to the same UI thread.
Other threads may be temporarily used, but you don't need to worry about that.
In particular, I/O on .NET generally goes through an I/O completion port that is part of the thread pool. I/O threads are automatically added and removed as necessary. Fairly often, the I/O has some additional work to do before it actually is ready to return to your code (e.g., parsing HTTP response headers), so a lot of the BCL I/O code will actually use the I/O thread just to queue work to the thread pool. So a thread pool worker thread is often used (briefly) by I/O code.
Also, in this particular example, I believe there's a separate timer thread as well, that coalesces system timers. Naturally, this is an implementation detail and subject to change.
So, in summary, other threads may be created/destroyed/temporarily used, but you don't have to worry about it. They're all managed by the BCL or .NET runtime in a very efficient manner, striking a balance between reusing threads (minimizing churn) and minimizing resource use (especially memory).
Calling async function in synchronous function doesn't wait for it to finish
Following Panagiotis Kanavos's advice:
Tasks aren't threads. There's no good reason to call Task.Start in
application code. How is checkInstalled called? That method should be
asynchronous itself, going all the way to the top-level event handler
that started these calls. The event handler should be async void but
all the other methods should be async Task. Calling .Wait() will
freeze the UI
After making every necessary function async and using await when needed I managed to get everything working the way I wanted
How to load a futurebuilder function after async await and then in flutter
I'm not exactly sure why you'd want to run it like that. You should call the getToken() function with async/await, and then async/await getSchool().
getData() async {
String token = await MainClass().getToken();
// We're passing 'token' from our first asynchronous call to getSchools()
List<SchoolInfo> schools = await getSchools(token);
}
Now you need to update getSchools
to accept this parameter.
Future<List<SchoolInfo>> getSchools(String tk) async {
... your code here
}
Related Topics
Get the Correct Week Number of a Given Date
When Is It Acceptable to Call Gc.Collect
How to Convert a Structure to a Byte Array in C#
Reading an Integer from User Input
What Are the Pros and Cons to Keeping SQL in Stored Procs Versus Code
Client to Send Soap Request and Receive Response
Multidimensional Array [][] VS [,]
How to Perform a Left Outer Join Using Linq Extension Methods
Check If a Class Is Derived from a Generic Class
Is "Else If" Faster Than "Switch() Case"
How to Get C# Enum Description from Value
Deserializing Json to .Net Object Using Newtonsoft (Or Linq to Json Maybe)