在HttpClient中使用await的异步调用永远不会返回

问题描述 投票:82回答:2

我正在Win8 CP上基于xaml的C# 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);

它挂在await但http呼叫实际上几乎立即返回(通过提琴手确认);好像await被忽略了它只是挂在那里。

在您询问之前 - 是 - 打开了专用网络功能。

任何想法为什么会挂起?

c# asynchronous async-await dotnet-httpclient
2个回答
120
投票

查看this answer我的问题似乎非常相似。

尝试的东西:在ConfigureAwait(false)返回的任务上调用GetStreamAsync()。例如。

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

这是否有用取决于你的代码是如何被调用的 - 在我的情况下,使用async调用Task.GetAwaiter().GetResult()方法导致代码挂起。

这是因为GetResult()阻止当前线程,直到任务完成。当任务完成时,它会尝试重新进入启动它的线程上下文,但不能,因为在该上下文中已经有一个线程,这被调用GetResult()阻塞了...死锁!

This MSDN post详细介绍了.NET如何同步并行线程 - 而the answer given to my own question提供了一些最佳实践。


1
投票

只是抬头 - 如果你错过了在ASP.NET控制器中的顶级等待,并且你返回任务而不是结果作为响应,它实际上只是在嵌套的await调用中挂起而没有错误。这是一个愚蠢的错误,但是如果我看到这篇文章,它可能会让我节省一些时间来查看代码中的奇怪内容。


0
投票

免责声明:我不喜欢ConfigureAwait()解决方案,因为我发现它不直观且难以记忆。相反,我得出的结论是在Task.Run(()=> myAsyncMethodNotUsingAwait())中包装无休止的方法调用。这似乎100%工作,但可能只是一个竞争条件!?我不太确定说实话是怎么回事。这个结论可能是错误的,我冒这个StackOverflow点的风险,希望从评论中学习:-P。请仔细阅读!

我刚刚遇到了问题并发现了更多信息here

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

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);
}

在我的情况下,我不关心调用非异步方法的结果,但我想在这个用例中很常见。您可以在调用异步方法中使用结果。

© www.soinside.com 2019 - 2024. All rights reserved.