-1

There is this question that mentions to use async/await to prevent deadlocks. In my case, this code works fine just for 2 or 3 elements in elementsInList variable showed below. I would like to know if there is a quantity factor that could affect to raise or not the deadlock.

I'm trying to use the following block of code:

var responses = elementsInList.AsParallel()
                    .WithDegreeOfParallelism(elementsInList.Count)
                    .Select(elementInList => GetResponse(elementInList, timeout)).ToList();

(...)

public JObject GetResponse(JObject request, TimeSpan timeout)
        {
              var endpoint = ConfigurationManager.AppSettings.Get("MyEndPoint");

                try
                {
                    using (var client = new HttpClient())
                    {
                        client.DefaultRequestHeaders.Accept.Clear();
                        client.Timeout = timeout;

                        IList<MediaTypeFormatter> formatters = GetMediaTypeFormatters();
                        HttpResponseMessage response = client.PostAsJsonAsync(EndPoint, request).Result;

                        if (response.IsSuccessStatusCode)
                        {
                            return response.Content.ReadAsAsync<T>(formatters).Result;
                        }

                        throw new Exception(response.ReasonPhrase);
                    }

                }
                catch (Exception ex)
                {
                    return new JObject()
                    {
                        new JProperty("Exception", "Invalid response. " + ex.Message)
                    };
                }
        } 

If I debug the code, when the .AsParallel line is hit, code gets blocked and it never returns an answer or goes to the next line. It seems a deadlock is happening there.

A detail I want to add, if I use this code with 2 or 3 elements, it works fine, however, when the call is like 30-50 elements, it fails. Is there a quantity factor that could raise the deadlock in the second scenario and not in the first one? I'm very curious if it affects or not, or if it also could be generated with only 2 or 3 elements.

Community
  • 1
  • 1
Alberto Montellano
  • 5,886
  • 7
  • 37
  • 53
  • `.Result` is essentially `.Wait` - make sure to read linked duplicate and if necessary update question/comment to reopen. – Alexei Levenkov Apr 06 '15 at 22:01
  • you are right, thanks for the link to other similar case. In my situation, the code showed works for a few elements. Is there a quantity factor that could raise/generate the deadlock? or even with 2 elements the deadlock must raise? Not sure why this code works for very little number of elements. – Alberto Montellano Apr 06 '15 at 23:23
  • 1
    It should deadlock as soon as thread running in ASP.Net/WinForms synchronization context makes call to Wait/Result. I'm not sure how `.AsParallel` runs separate actions in parallel, but I assume some would run on current UI thread and cause deadlock. Checking callstacks when it hangs can reveal the exact source of the problem. – Alexei Levenkov Apr 06 '15 at 23:58

1 Answers1

2

As a general rule of thumb, only use parallel technologies if your app needs to do CPU-intensive work. For I/O-bound work, use asynchronous technologies. For the extremely rare situation where you need both parallel and asynchronous code, consider a technology like TPL Dataflow.

In your case, there's no CPU-intensive work to be done, so remove all the parallel stuff and just use async/await instead:

public async Task<JObject> GetResponseAsync(JObject request, TimeSpan timeout)
{
  var endpoint = ConfigurationManager.AppSettings.Get("MyEndPoint");
  try
  {
    using (var client = new HttpClient())
    {
      client.DefaultRequestHeaders.Accept.Clear();
      client.Timeout = timeout;

      IList<MediaTypeFormatter> formatters = GetMediaTypeFormatters();
      HttpResponseMessage response = await client.PostAsJsonAsync(EndPoint, request);

      if (response.IsSuccessStatusCode)
        return await response.Content.ReadAsAsync<T>(formatters);

      throw new Exception(response.ReasonPhrase);
    }
  }
  catch (Exception ex)
  {
    return new JObject()
    {
      new JProperty("Exception", "Invalid response. " + ex.Message)
    };
  }
}

Usage:

var responses = await Task.WhenAll(
    elementsInList.Select(x => GetResponseAsync(x, timeout)));
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Thank you very much for your answer. Does it mean when we use HttpClient ReadAsAsync with more than 1 thread/task we need to use async/await in a mandatory way ? Or there could be cases where no deadlock occurs? – Alberto Montellano Apr 08 '15 at 16:57
  • @AlbertoMontellano: If you're using `HttpClient`, you should be using `await`. The number of threads/tasks has nothing to do with it. Yes, there are situations where no deadlocks occur, but you shouldn't depend on them. – Stephen Cleary Apr 08 '15 at 17:32
  • Thank you for your guidelines and clarifications about it. – Alberto Montellano Apr 08 '15 at 17:36