Api 和控制台应用程序中的异步行为上下文有何不同

How context differs in the async behaviour in Api and in Console Application

如果我 运行 .net web 中的此代码 api

public class ConfigurationApiClient : IConfigurationService
{
    private readonly HttpClient _client;

    public ConfigurationApiClient(IConfigurationManager configurationManager)
    {
        ;
        _client = new HttpClient();
        _client.BaseAddress = new Uri(configurationManager.GetAppSetting("ApiUrl"));
        _client.DefaultRequestHeaders.Accept.Clear();
        _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    }

    public Configuration GetConfiguration(int agentId)
    {
        return GetConfigurationAsync(agentId).Result;
    }

    private async Task<Configuration> GetConfigurationAsync(int agentId)
    {
        try
        {
            var response = await _client.GetAsync("resource").ConfigureAwait(false);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsAsync<Configuration>();
        }
        catch (HttpRequestException ex)
        {
            throw new Exception(string.Format("Exception when getting  configuration for agent: {0}", agentId), ex);
        }
    }

}

我依靠 ConfigureAwait(false) 来让它工作,如果我删除它,它就会进入死锁状态。

但如果基于此 example 我在控制台应用程序中编写了与前一个等效的内容

class Program
{
    static void Main()
    {
        var product = RunAsync().Result;
    }

    static async Task<Product> RunAsync()
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("http://localhost:8080/");
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));


            // New code:
            HttpResponseMessage response = await client.GetAsync("api/resource");
            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsAsync<Product>();
            }

            else
            {
                return null;
            }
        }
    }
}

即使我阅读了 ConfigureAwait(false) 我也不清楚为什么它在控制台中工作而不是在 api 中工作。

除非 ConfigureAwait(false) 另有说明,否则使用等待捕获 SynchronizationContext.Current。当 await 之后的代码恢复时,它被发布到该上下文中。在 UI 应用程序中,这意味着 运行 在单个 UI 线程上。在 asp.net 个应用程序(您的案例)中,它在原始请求的上下文中运行。

控制台应用程序没有任何 SynchronizationContext 开头,因此您不会死锁。

你可以自己看看里面有什么SynchronizationContext.Current

但是,使用 ConfigureAwait(false) 只能作为最后的措施。大多数情况下,您希望避免阻塞异步代码。