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?