Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

await and deadlock prevention - clarification?

I read this article about Task.ConfigureAwait which can help to prevent deadlocks in async code.

Looking at this code: (I know I shouldn't do .Result , But it's a part of the question)

private void Button_Click(object sender, RoutedEventArgs e)
{
    string result = GetPageStatus().Result;
    Textbox.Text = result;
}
public async Task<string> GetPageStatus()
{
    using (var httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync("http://www.google.com");
        return response.StatusCode.ToString();
    }
}

This will result a deadlock because :

  1. The .Result - operation will then block the current thread (i.e the UI thread) while it waits for the async operation to complete.

  2. Once the network call is complete it will attempt to continue executing the response.StatusCode.ToString() - method on the captured context. (which is blocked - hence deadlock).

One solution was to use :

var response = await httpClient.GetAsync("http://www.google.com").ConfigureAwait(false);

But other solution was to async all the way ( without blocking) :

/*1*/   private async void Button_Click(object sender, RoutedEventArgs e)
/*2*/   {
/*3*/       string result = await GetPageStatus();
/*4*/       Textbox.Text = result;
/*5*/   }
/*6*/   public async Task<string> GetPageStatus()
/*7*/   {
/*8*/       using (var httpClient = new HttpClient())
/*9*/       {
/*10*/          var response = await httpClient.GetAsync("http://www.google.com");
/*11*/          return response.StatusCode.ToString();
/*12*/      }
/*13*/   }

Question :

(I'm trying to understand how this code helps to solve the problem - via context POV).

  1. Does line #3 and line #10 captures different contexts?

  2. Am I right regarding the way of flow as I think it is:

    • line #3 calls #6 (which calls #10) and sees that it didn't finish yet, so it awaits ( captured context for #3 = UI thread).

    • Later on, line #10 capture another context (I will call it newContext) after it finishes, it is back to "newContext" and then releases the UI context(thread).

Was I right?

  1. Visualization: (is this the right flow?)

Code execution flow

like image 983
Royi Namir Avatar asked Dec 24 '14 12:12

Royi Namir


1 Answers

Does line #3 and line #10 captures different contexts ?

As the way your code looks, no. They both will capture the same UI synchronization context, as you don't use ConfigureAwait(false) which would prevent the marshaling of the continuation back to the UI context.

Am I right regarding the way of flow as I think it is :

line #3 calls #6 (which calls #10) and sees that it didnt finish yet , so it awaits ( captured context for #3= UI thread).

Later on , line #10 capture another context ( I will call it newContext) after it finishes , it is back to "newContext" and then releases the UI context(thread).

Almost. There is no "new context" being created in your call. It is always the same UI synchronization context. If, for example, you had two async calls one after the other, when one used ConfigureAwait(false), the second call would continue its execution on a thread pool thread.

As for your visualization, it does capture properly the execution flow of your code.

like image 84
Yuval Itzchakov Avatar answered Oct 12 '22 21:10

Yuval Itzchakov