What is the best practice to call Async method from Sync method? [duplicate]
Asked Answered
D

1

10

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?

Disaccharide answered 8/7, 2021 at 11:44 Comment(5)
Best practice is to not wrap async calls into sync calls (as it's stated in answer where 3rd option came)Unplaced
^^ And he says: Don't. (Except you absolutely have to.)Rotberg
Does this answer your question? How to call asynchronous method from synchronous method in C#?Weedy
@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.Disaccharide
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.Weedy
B
9

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.

Barbiturism answered 8/7, 2021 at 13:57 Comment(4)
no best practice with NET 5, NET 6 , ...?Stereograph
@Stereograph No. There is no solution that can work in all situations.Barbiturism
Are there solutions for any situations? Not for all situations, for some situations.Stereograph
@Kiquenet: Yes; see the article linked in my answer.Barbiturism

© 2022 - 2024 — McMap. All rights reserved.