8

I know some people will argue "why don't you just make SyncMethod() to async method?". We wish, but in a real world, sometimes we have to keep SyncMethod the way it is for backwards compatibility reasons.

Here is the situation. We have a deadlock with the following code.

public void SyncMethod()
{
    var serviceResult = ProcessDataAsync().Result;
}

public await ServiceResult ProcessDataAsync()
{
    //Do other things
    await GetDataFromApiAsync();
}

private static HttpClient Client = new HttpClient(); 
public await ApiResult GetDataFromApiAsync()
{
    var response = await Client.GetAsync("http://api.com/getJson");
    dynamic obj = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());
    return obj;  
}

Option 1: Use Task.Run and get task.Result. This solves the deadlock issue but it's forced to run in a new thread, outside of the synchronization context of the originating thread. However, there's certain environments where this is very ill-advised: particularly web applications. Is it a good practice?

public void SyncMethod()
{
    Task<decimal> task = Task.Run<decimal>(async () => await ProcessDataAsync());
    var serviceResult = task.Result;
}

Optoin 2: add ConfigureAwait(false) all the way down from Sync to the last async method.

public void SyncMethod()
{
    var serviceResult = GetDataFromApiAsync().ConfigureAwait(false).GetAwaiter().GetResult();;
}

public await ServiceResult ProcessDataAsync()
{
    //Do other things
    await GetDataFromApiAsync().ConfigureAwait(false);
}

private static HttpClient Client = new HttpClient(); 
public await ApiResult GetDataFromApiAsync()
{
    var response = await Client.GetAsync("http://api.com/getJson").ConfigureAwait(false);
    dynamic obj = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync().ConfigureAwait(false));
    return obj;  
}

Option 3: Stephen Cleary's answer.

public void SyncMethod()
{
    var serviceResult = GetDataFromApiAsync().(sync: true).GetAwaiter().GetResult();
}

All these solutions will solve the deadlock issue. My question is what the best practice would be?

Jonas T
  • 2,989
  • 4
  • 32
  • 43
  • 4
    Best practice is to not wrap async calls into sync calls (as it's stated in answer where 3rd option came) – Selvin Jul 08 '21 at 11:47
  • 2
    ^^ And he says: Don't. (Except you absolutely have to.) – Fildor Jul 08 '21 at 12:17
  • 1
    Does this answer your question? [How to call asynchronous method from synchronous method in C#?](https://stackoverflow.com/questions/9343594/how-to-call-asynchronous-method-from-synchronous-method-in-c) – Franz Gleichmann Jul 08 '21 at 12:38
  • @FranzGleichmann, of course, I've seen that question before I made this post. No, it doesn't answer my question but it add more options to my question. – Jonas T Jul 08 '21 at 13:12
  • 1
    well, then my view is: if you have any technical constraints that make the answer obvious, then use that option. if not, then: all options have pros and cons, and this question is _opinion-based_. – Franz Gleichmann Jul 08 '21 at 13:14

1 Answers1

8

what the best practice would be?

There is no best practice, and here's why:

Every hack works in some situations and does not work in other situations. There is no hack that works in all situations. If there was a hack that worked everywhere, then that would be what everyone would use and that hack would be the best practice. But there isn't a hack that works everywhere, so there is no "best practice". There is no general-purpose solution.

The various hacks are described in this article, along with a description of the situations where each one works and doesn't.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810