12

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?

5
  • 4
    Best practice is to not wrap async calls into sync calls (as it's stated in answer where 3rd option came) Commented Jul 8, 2021 at 11:47
  • 2
    ^^ And he says: Don't. (Except you absolutely have to.) Commented Jul 8, 2021 at 12:17
  • 1
    Does this answer your question? How to call asynchronous method from synchronous method in C#? Commented Jul 8, 2021 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. Commented Jul 8, 2021 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. Commented Jul 8, 2021 at 13:14

1 Answer 1

10

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.

Sign up to request clarification or add additional context in comments.

4 Comments

no best practice with NET 5, NET 6 , ...?
@Kiquenet No. There is no solution that can work in all situations.
Are there solutions for any situations? Not for all situations, for some situations.
@Kiquenet: Yes; see the article linked in my answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.