I have a small project - WinForms On .net frameWork - just a small test :
private void button9_Click(object sender, EventArgs e)
{
string text = GetTitleAsync().Result;
button9.Text = text;
}
private async Task<string> GetTitleAsync()
{
await Task.Delay(3000);
return "Hello!";
}
As I ran the application , Clicking the button: "button9" - caused a dead lock, (since the thread was hung on the ".result" )
Writing GetTitleAsync() this way:
private async Task<string> GetTitleAsync()
{
await Task.Delay(3000).ConfigureAwait(false);
return "Hello!";
}
solved the deadlock - and the application ran ok.
But I don't understand how ?
I would have expected, that using ".ConfigureAwait(false)" would cause a situation in which :
"button9.Text = text;" is executed on a different thread than the one, on which the UI was created, and an excpetion would be throwed !
but it works excellent ! how??
I would have expected, that using ".ConfigureAwait(false)" would cause a situation in which "button9.Text = text;" is executed on a different thread than the one, on which the UI was created, and an excpetion would be throwed ! but it works excellent ! how??
I recommend reading my async
/await
intro; I try to include everything you need to know about async
/await
and their contexts, without getting into too much detail for an intro.
Specifically, there are two points from that post that are worth noting:
async
method begins executing synchronously, on the calling thread.await
captures a context unless you use ConfigureAwait(false)
.So, walking through this code:
private void button9_Click(object sender, EventArgs e)
{
string text = GetTitleAsync().Result;
button9.Text = text;
}
private async Task<string> GetTitleAsync()
{
await Task.Delay(3000).ConfigureAwait(false);
return "Hello!";
}
This is what happens, in order, with special attention paid to which thread runs which code:
button9_Click
calls GetTitleAsync()
on the UI thread.GetTitleAsync()
calls Task.Delay(3000)
and gets back a task that will complete in 3 seconds.GetTitleAsync()
calls ConfigureAwait(false)
and gets back a configured awaiter that will not resume on the current (UI) context.GetTitleAsync()
uses await
to asynchronously wait for the task to complete. This await
will not resume on the current (UI) context because the await
has been configured not to.await
examines the task and sees it is not complete, so it returns an incomplete Task<string>
to its caller.button9_Click
calls .Result
on that task. This blocks the UI thread until that task completes (i.e., GetTitleAsync()
is finished executing).Task.Delay(3000)
completes.GetTitleAsync()
resumes executing after its await
. Since this was a configured await
, it continues executing on a thread pool thread.GetTitleAsync()
returns "Hello!"
. This is done on a thread pool thread.GetTitleAsync()
is now complete, and the Task<string>
that it returned earlier is now completed with a result value. This completion also happens on a thread pool thread.button9_Click
.button9_Click
executes button9.Text = text;
on the UI thread.