我对Task和async / await模式有很好的理解,但是最近有人修复了他所说的由以下原因造成的死锁:
public async Task<HttpResponseMessage> GetHttpResponse()
{
using (var client = new HttpClient())
{
return await client.SendAsync(new HttpRequestMessage()).ConfigureAwait(false);
}
}
他说他用某种Task.Factory.StartNew模式修复了它.
public HttpResponseMessage GetHttpResponse()
{
using (var client = new HttpClient())
{
return Task.Factory.StartNew(() => client.SendAsync(new HttpRequestMessage()).Result).Result;
}
}
首先,问题是我为什么要将return语句更改为HttpResponseMessage而不是Task< HttpResponseMessage>.
我的第二个问题是,为什么这段代码会解决死锁错误.根据我的理解,他调用.Result的事实迫使GetHttpResponse线程等待(并冻结),直到client.SendAsync完成.
任何人都可以尝试解释这个代码如何影响任何TaskScheduler和SynchronizationContext.
谢谢您的帮助.
编辑:这是调用方法,为问题提供更多的上下文
public IWebRequestResult ExecuteQuery(ITwitterQuery twitterQuery, ITwitterClientHandler handler = null)
{
HttpResponseMessage httpResponseMessage = null;
try
{
httpResponseMessage = _httpClientWebHelper.GetHttpResponse(twitterQuery, handler).Result;
var result = GetWebResultFromResponse(twitterQuery.QueryURL, httpResponseMessage);
if (!result.IsSuccessStatusCode)
{
throw _exceptionHandler.TryLogFailedWebRequestResult(result);
}
var stream = result.ResultStream;
if (stream != null)
{
var responseReader = new StreamReader(stream);
result.Response = responseReader.ReadLine();
}
return result;
}
// ...
最佳答案 死锁不是由返回await client.SendAsync(new HttpRequestMessage()).ConfigureAwait(false);引起的.这是由于阻塞调用堆栈引起的.由于HttpClient.SendAsync()的MS实现不太可能内部有一些阻塞代码(可能会死锁),因此它必须是公共异步的调用者之一Task< HttpResponseMessage> GetHttpResponse()在返回的Task上使用.Wait()或.Result.你的同事所做的就是将阻塞调用移动到堆栈中,在那里它更加明显.此外,这个“修复”甚至不能解决传统的死锁,因为它使用相同的同步上下文(!).你可以在堆栈的其他地方推断出一些其他函数卸载没有asp.net同步上下文的新任务,并且在新的(可能是默认的)上下文中阻塞GetHttpResponse()执行,否则“修复”也会出现死锁!
因为在现实世界中,并不总是可以将生成遗留代码重构为异步,所以您应该使用自己的异步HttpClient接口,并确保实现使用.ConfigureAwait(false),因为所有基础结构库都应该如此.