Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding Deadlock with HttpClient

Tags:

c#

What is the best way to use HttpClient and avoid deadlock? I am using the code below, called entirely from synchronous methods, but I concerned it maybe causing a deadlock.

I've done some reading on functions like .ConfigureAwait(false), .GetAwaiter(), .GetResult() but I am looking for input on the best practice approach.

Not quite exact code, but close enough.

public static bool TryRequest(string url, out response)
{    
    HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
    using (HttpClient client = new HttpClient())
    {
       HttpResponseMessage responseMessage = null;

       switch (verb)
       {
          case HttpVerb.Put:
             responseMessage = client.PutAsync(url, content).Result;
             break;
          case HttpVerb.Post:
             responseMessage = client.PostAsync(url, content).Result;
             break;
          case HttpVerb.Delete:
             responseMessage = client.DeleteAsync(url).Result;
             break;
          case HttpVerb.Get:
             responseMessage =  client.GetAsync(url).Result;
             break;
       }

       if (responseMessage.IsSuccessStatusCode)
       {
          responseContent = responseMessage.Content.ReadAsStringAsync().Result;
          statusCode = responseMessage.StatusCode;
       }
    }
}
like image 459
Chris Morgan Avatar asked Jan 08 '23 17:01

Chris Morgan


1 Answers

It looks like you are trying to run asynchronous code synchronously.

With WinForms and WPF YOU CANNOT SAFELY DO THIS!

The only thing you can do is to use async all the way up. Its a known problem with .net async. You can use public async void XXX() methods. But then you don't know when they complete. You should ONLY ever use async void when coupled with an event handler.

The reason you are getting deadlocks is that the default TaskFactory will try to marshal interupt callbacks back to the SynchronizationContext, which is likely your UI thread.

Even if you use Task.ConfigureAwait(false) there is no guarantee that further down the callstack you don't have a callback which expects the UI thread.

As long as you block the SynchronizationContext thread, there is a very high possibility that you will deadlock.

It is also worth noting that it is possible that the asynchronous code seems to sometimes work. This is because, an async method that returns a Task, is allowed to synchronously complete (for example Task.Return<T>(T result)). This will often happen with methods that have a cache (like HttpRequests).

EDIT: @SriramSakthivel suggest that you can run an async method synchronously by wrapping it within Task.Run. This is because Task.Run will run the code without the parent SynchronizationContext.

Task.Run(RunRequest).Result;

I personally do not recommend this, as it relies on the specific implementation of Task.Run along with TaskFactory to work. It is entirely possible (but unlikely) that a new version of .net will break this piece of code.

like image 108
Aron Avatar answered Jan 17 '23 15:01

Aron