c# – 在Task.Factory.StartNew中封装异步方法

我对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),因为所有基础结构库都应该如此.

点赞