从 Sync 方法调用 Async 方法的最佳做法是什么?

What is the best practice to call Async method from Sync method?

我知道有些人会争论“为什么不直接将 SyncMethod() 设为异步方法?”。我们希望,但在现实世界中,有时出于向后兼容的原因,我们必须保持 SyncMethod 的原样。

情况是这样的。我们遇到了以下代码的死锁。

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;  
}

选项 1:使用 Task.Run 并得到 task.Result。这解决了死锁问题,但它被迫 运行 在原始线程的同步上下文之外的新线程中。但是,在某些环境中这是非常不明智的:尤其是 Web 应用程序。这是一个好习惯吗?

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

Optoin 2:从 Sync 一直添加 ConfigureAwait(false) 到最后一个异步方法。

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;  
}

选项 3Stephen Cleary's answer

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

所有这些解决方案都将解决死锁问题。我的问题是最佳做法是什么?

what the best practice would be?

没有最佳实践,原因如下:

每个 hack 在某些情况下有效,而在其他情况下无效。没有适用于所有情况的 hack。如果有一种适用于任何地方的 hack,那么每个人都会使用它,而这种 hack 将是最佳实践。但是没有一种适用于任何地方的黑客,所以没有“最佳实践”。 没有通用解决方案。

various hacks are described in this article,以及对每种情况有效和无效的情况的描述。