Deadlock when blocking async HttpClient call in task continuation

I'm trying to wrap my head around synchronously calling asynchronous functions and deadlock.

In the example below, I am blocking an asynchronous call that internally uses ConfigureAwait (false), which as far as I can tell should prevent deadlocks. The first call to the web service is not stalled. However, there is a second one that happens in the sequel, which syncs with dead ends in the UI.

GetBlocking_Click is a click event in a WPF application, so both the first and second requests happen on the UI thread.

      private void GetBlocking_Click(object sender, RoutedEventArgs e)
    {
        const string uri = "http://www.mywebservice.com";
        var example1 = GetAsync(uri).Result;

        Task.FromResult(true)
            .ContinueWith(t =>
            {
                var example2 = GetAsync(uri).Result;
            }, 
            TaskScheduler.FromCurrentSynchronizationContext()).Wait();
    }

    private async Task<string> GetAsync(string url)
    {
        using (var client = new HttpClient())
        {
            var responseMessage = await client.GetAsync(url).ConfigureAwait(false);

            return await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
        }
    }

      

Could you please explain what is the difference between these two calls?

+3


source to share


1 answer


You are on the UI thread. From this thread, you call Task.FromResult

and create a task. Then you add a continuation to this task, which should be done on the UI thread. This continuation is added to the message processing queue.

Then you wait for this continuation to complete on the UI thread. This continuation waits for the UI to be available in order to even trigger the continuation body that will eventually invoke GetAsync

. This is a dead end.

It doesn't matter what the continuation body is; the following code locks are for the same reason:



private void GetBlocking_Click(object sender, RoutedEventArgs e)
{
    Task.FromResult(true)
        .ContinueWith(t =>{ }, 
            TaskScheduler.FromCurrentSynchronizationContext())
        .Wait();
}

      

As for the fix, you just shouldn't be waiting for asynchronous operations synchronously. Either make everything asynchronous or make everything synchronous.

+5


source







All Articles