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)
只能作为最后的措施。大多数情况下,您希望避免阻塞异步代码。
如果我 运行 .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)
只能作为最后的措施。大多数情况下,您希望避免阻塞异步代码。