为什么HTTP请求永远不会在异步等待状态下返回?

时间:2018-08-09 10:57:02

标签: c# asp.net asynchronous async-await task-parallel-library

我正在尝试从在IIS 8.5上运行的ASP.NET应用程序内对另一台服务器进行HTTP调用。

首先,我从Microsoft的Call a Web API From a .NET Client (C#)的文章中获得了一些提示。 我可以很容易地看到它们在此处进行HTTP调用的方式。仅显示一个简化的示例:

    static async Task<Product> GetProductAsync(string path)
    {
        HttpResponseMessage response = await client.GetAsync(path);
        if (response.IsSuccessStatusCode)
        {
            // retrieve response payload
            ... = await response.Content.ReadAsAsync<...>();
        }
        // do something with data
    }

我想这很容易,所以我很快为我的应用程序编写了一个类似的方法(请注意,ReadAsAsync扩展方法appears to require an additional library,所以我选择了一种更抽象的内置方法,但是否则可能是类似的方法):

    private async Task<MyInfo> RetrieveMyInfoAsync(String url)
    {
        var response = await HttpClient.GetAsync(url);

        response.EnsureSuccessStatusCode();
        var responseBody = await response.Content.ReadAsStringAsync();

        return JsonConvert.DeserializeObject<MyInfo>(responseBody);
    }

不幸的是,调用此方法将导致我的应用程序挂起。调试时,事实证明对await的{​​{1}}调用永远不会返回。

经过一番搜索,我偶然发现了remotely similar issue,在其评论部分中,我通过a very interesting suggestion找到了Mr. B

  

删除所有异步内容并确保其有效。

所以我尝试了:

GetAsync

(对我而言),这确实令人惊讶。 private Task<MyInfo> RetrieveMyInfoAsync(String url) { return HttpClient.GetAsync(url).ContinueWith(response => { response.Result.EnsureSuccessStatusCode(); return response.Result.Content.ReadAsStringAsync(); }).ContinueWith(str => JsonConvert.DeserializeObject<MyInfo>(str.Result.Result)); } 在不到一秒钟的时间内从另一台服务器返回了预期的响应。

现在,同时使用AngularJS,我对GetAsyncresponse.Result.Content之类的东西感到有些失望。在AngularJS中,我希望上面的调用就像这样:

str.Result.Result

即使我们不喜欢JavaScript中发生的自动JSON反序列化,AngularJS代码仍然更容易实现,例如$http.get(url).then(function (response) { return response.data; }); 并没有包装在一个Promise之类的东西中,当从延续函数中返回另一个Promise时,我也不会得到像response这样的结构。

因此,如果使用Task<Task<...>>语法而不是更具可读性的async-await模式(如果后者有效的话),我感到不满意。

我的C#HTTP调用的async-await变体在做什么?

1 个答案:

答案 0 :(得分:4)

因此,从ConfigureAwait(false)有助于解决您的问题的角度来看,请阅读Stephen Cleary博客中的内容: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

这家伙几乎是async专家(他写了《 C#Cookbook中的并发性》一书),所以我能说的他可能解释得更好。基本上,您是在某个地方阻塞ASP.NET线程,可能不是一直使用await,而是使用WaitResultGetResult()。您应该可以使用该博客自己诊断问题。

ConfigureAwait(false)所做的是,它无法捕获当前上下文,因此HTTP请求在ASP.NET上下文以外的其他位置(正确)执行,从而避免了死锁。

编辑:

根据您的评论,

GetAwaiter().GetResult()是导致此问题的原因。如果将其更改为await,并将调用方法更改为async,则可能会修复所有问题。

自从C#7.0和async Task Main()方法支持以来,实际上根本没有理由阻塞而不是在应用程序代码中使用await