Reputation: 11301
I was reading about asynchronous function calls on Asynchronous Programming with Async and Await.
At the first example, they do this, which I get:
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
string urlContents = await getStringTask;
But then they explain that if there's not any work to be done in the mean time, you can just do it like this:
string urlContents = await client.GetStringAsync();
From what I understand, the await
keyword will suspend the code flow until the function returns. So how is this different from the synchronous call below?
string urlContents = client.GetString();
Upvotes: 48
Views: 21376
Reputation: 25211
Calling await client.GetStringAsync()
yields the execution to the calling method, which means it won't wait for the method to finish executing, and thus won't block the thread. Once it's done executing in the background, the method will continue from where it stopped.
If you just call client.GetString()
, the thread's execution won't continue until this method finished executing, which will block the thread and may cause the UI to become unresponsive.
Example:
public void MainFunc()
{
InnerFunc();
Console.WriteLine("InnerFunc finished");
}
public async Task InnerFunc()
{
// This causes InnerFunc to return execution to MainFunc,
// which will display "InnerFunc finished" immediately.
string urlContents = await client.GetStringAsync();
// Do stuff with urlContents
}
public void InnerFunc()
{
// "InnerFunc finished" will only be displayed when InnerFunc returns,
// which may take a while since GetString is a costly call.
string urlContents = client.GetString();
// Do stuff with urlContents
}
Upvotes: 29
Reputation: 7622
From what I understand, the await keyword will suspend the code flow until the function returns
Well, Yes and No.
client.GetString()
will block the thread).In fact, it will return to its calling method. To understand what it means by return to its calling method, you can read about another C# compiler magic - the yield return
statement.
Iterator blocks with yield return
will break the method into a state machine - where code after the yield return
statement will execute only after MoveNext()
is called on the enumerator. (See this and this).
Now, async/await
mechanism is also based on similar state machine (however, its much more complicated than the yield return
state machine).
To simplify matters, lets consider a simple async method:
public async Task MyMethodAsync()
{
// code block 1 - code before await
// await stateement
var r = await SomeAwaitableMethodAsync();
// code block 2 - code after await
}
async
identifier you tell the compiler to break the method into a state machine and that you are going to await
inside this method.Thread1
and your code calls this MyMethodAsync()
. Then code block 1
will synchronously run on the same thread. SomeAwaitableMethodAsync()
will also be called synchronously - but lets say that method starts a new asynchronous operation and returns a Task
. await
comes into picture. It will return the code flow back to its caller and the thread Thread1
is free to run callers code. What happens then in calling method depends on whether calling method await
s on MyMethodAsync()
or does something else - but important thing is Thread1
is not blocked.SomeAwaitableMethodAsync()
eventually completes, the code block 2
is Scheduled to run.async/await
is built on the Task parallel library - so, this Scheduling is done over TPL.code block 2
may not be scheduled over the same thread Thread1
unless it had an active SynchronizationContext
with thread affinity (like WPF/WinForms UI thread). await
is SynchronizationContext
aware, so, code block 2
is scheduled over same SynchronizationContext
, if any, when the MyMethodAsync()
was called. If there was no active SynchronizationContext
, then with all possibility, code block 2
will run over some different thread.Lastly, I will say that since async/await
is based on state machine created by compiler, like yield return
, it shares some of the shortcomings - for example, you cannot await
inside a finally
block.
I hope this clears your doubts.
Upvotes: 18