HttpClient中带有await的异步调用永远不会返回


95

我正在C#Win8 CP上的一个基于xaml的Metro应用程序中打个电话。此调用仅命中Web服务并返回JSON数据。

HttpMessageHandler handler = new HttpClientHandler();

HttpClient httpClient = new HttpClient(handler);
httpClient.BaseAddress = new Uri("http://192.168.1.101/api/");

var result = await httpClient.GetStreamAsync("weeklyplan");
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(WeeklyPlanData[]));
return (WeeklyPlanData[])ser.ReadObject(result);

它挂在,awaithttp调用实际上几乎立即返回(通过提琴手确认);好像await被忽略了,就挂在那里。

询问之前 -是-专用网络功能已打开。

任何想法为什么会挂起?


1
您如何调用该async方法?它不会引发异常吗?
svick 2012年

Answers:


136

看看这个问题的答案,这似乎非常相似。

可以尝试的方法:调用ConfigureAwait(false)由返回的Task GetStreamAsync()。例如

var result = await httpClient.GetStreamAsync("weeklyplan")
                             .ConfigureAwait(continueOnCapturedContext:false);

这是否有用取决于您上面的代码的调用方式-在我的情况下,调用asyncusing 的方法会Task.GetAwaiter().GetResult()导致代码挂起。

这是因为GetResult()阻塞当前线程,直到任务完成。当任务完成时,它会尝试重新进入启动该线程的线程上下文,但由于在该上下文中已经存在一个线程,因此无法进入该线程上下文,该线程被对GetResult()... 死锁的调用所阻止

这篇MSDN帖子详细介绍了.NET如何同步并行线程- 对我自己的问题的回答给出了一些最佳实践。


12
谢谢,几乎没有看到异步/等待。
2013年

4
我也是!为什么没有更好地记录这些资料?再次感谢
Avrohom Yisroel 2014年

1
如果既不在UI上下文中又不在ASP.NET上下文中,它将发生吗?
machinarium

1
很棒的答案!但是我很困惑,为什么到目前为止我在使用HttpClient时仅遇到此问题,似乎HttpClient中的基础实现未正确实现。我发现的其他解决方法包括将当前线程设置为STA,这很有帮助,但实际上是间接的,尤其是当您使用第3方程序集并且不知道在幕后某些调用肯定会等待它的响应时永远不会得到。在我的情况下,dll是内部的,因此我们能够执行ConfigureAwait ...,但必须在HttpClient obj的最低级别进行。
克里斯·谢

2
@ChrisSchaller确保阅读stackoverflow.com/a/10351400/174735上的完整答案,该答案相当完整地解释了该问题。
本杰明·福克斯

5

请注意-如果您错过了ASP.NET控制器顶层的等待,并且返回了任务而不是结果作为响应,则它实际上只是挂在嵌套的await调用中而没有任何错误。一个愚蠢的错误,但是如果我看过这篇文章,它可能节省了我一些时间来检查代码是否有奇怪的地方。


0

免责声明:我不喜欢ConfigureAwait()解决方案,因为我发现它不直观且难以记住。相反,我得出了将未等待的方法调用包装在Task.Run(()=> myAsyncMethodNotUsingAwait())中的结论。这似乎可以100%起作用,但可能只是比赛条件!?我不太确定这是怎么回事。这个结论可能是错误的,我冒着我在此处的StackOverflow风险,希望从评论中学习:-P。请阅读它们!

我只是遇到了所描述的问题,并在此处找到了更多信息。

声明是:“您不能调用异步方法”

await asyncmethod2()

从阻止的方法

myAsyncMethod().Result

就我而言,我无法更改调用方法,并且它不是异步的。但是我实际上并不关心结果。我记得它也无法删除.Result并丢失等待。

所以我这样做:

public void Configure()
{
    var data = "my data";
    Task.Run(() => NotifyApi(data));
}

private async Task NotifyApi(bool data)
{
    var toSend = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
    await client.PostAsync("http://...", data);
}

就我而言,我不在乎调用非异步方法的结果,但我猜在这种用例中这很普遍。您可以在调用异步方法中使用结果。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.