0
votes

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??

1
Don't Block on Async Code -- Remove .ConfigureAwait(false); from there, change: private async void button9_Click and string text = await GetTitleAsync(); - Jimi
ConfigureAwait(false); causes the continuation to resume on a different Thread, but the WindowsFormsSynchronizationContext is strong, the first await will resume in the UI Thread. So will do Result. But you don't block on async code. - Jimi
I also recommend reading Stephen Cleary's posts on the topic (link in the first comment). I also have to explained it in this answer: How ConfigureAwait(false) Prevent UI Deadlocks. You may find it useful. - Reza Aghaei

1 Answers

0
votes

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:

  • Every 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:

  1. button9_Click calls GetTitleAsync() on the UI thread.
  2. GetTitleAsync() calls Task.Delay(3000) and gets back a task that will complete in 3 seconds.
  3. GetTitleAsync() calls ConfigureAwait(false) and gets back a configured awaiter that will not resume on the current (UI) context.
  4. 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.
  5. The await examines the task and sees it is not complete, so it returns an incomplete Task<string> to its caller.
  6. button9_Click calls .Result on that task. This blocks the UI thread until that task completes (i.e., GetTitleAsync() is finished executing).
  7. Three seconds later, the task returned from Task.Delay(3000) completes.
  8. GetTitleAsync() resumes executing after its await. Since this was a configured await, it continues executing on a thread pool thread.
  9. GetTitleAsync() returns "Hello!". This is done on a thread pool thread.
  10. By returning a value, 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.
  11. Since the task is now complete, the UI thread is no longer blocked, and it continues executing button9_Click.
  12. button9_Click executes button9.Text = text; on the UI thread.