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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9895048/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 11:06:53  来源:igfitidea点击:

Async call with await in HttpClient never returns

c#asynchronousasync-awaitdotnet-httpclient

提问by keithwarren7

I have a call I am making from inside a xaml-based, C#metro application on the Win8 CP; this call simply hits a web service and returns JSON data.

我正在从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);

It hangs at the awaitbut the httpcall actually returns almost immediately (confirmed through fiddler); it is as if the awaitis ignored and it just hangs there.

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

Before you ask- YES - the Private Network capability is turned on.

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

Any ideas why this would hang?

任何想法为什么会挂起?

回答by Benjamin Fox

Check out this answerto my question which seems to be very similar.

查看这个对我的问题的回答,这似乎非常相似。

Something to try: call ConfigureAwait(false)on the Task returned by GetStreamAsync(). E.g.

尝试一下:调用ConfigureAwait(false)GetStreamAsync(). 例如

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

Whether or not this is useful depends on how your code above is being called - in my case calling the asyncmethod using Task.GetAwaiter().GetResult()caused the code to hang.

这是否有用取决于你上面的代码是如何被调用的——在我的例子中调用async方法 usingTask.GetAwaiter().GetResult()导致代码挂起。

This is because GetResult()blocks the current thread until the Task completes. When the task does complete it attempts to re-enter the thread context in which it was started but cannot because there is already a thread in that context, which is blocked by the call to GetResult()... deadlock!

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

This MSDN postgoes into a bit of detail on how .NET synchronizes parallel threads - and the answer given to my own questiongives some best practices.

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

回答by CodingYourLife

Disclaimer: I don't like the ConfigureAwait() solution because I find it non-intuitive and hard to remember. Instead I came to the conclusion to wrap non awaited method calls in Task.Run(() => myAsyncMethodNotUsingAwait()). This seems to work 100% but might just be a race condition!? I'm not so sure what is going on to be honest. This conclusion might be wrong and I risk my StackOverflow points here to hopefully learn from the comments :-P. Please do read them!

免责声明:我不喜欢 ConfigureAwait() 解决方案,因为我发现它不直观且难以记住。相反,我得出的结论是将非等待的方法调用包装在 Task.Run(() => myAsyncMethodNotUsingAwait()) 中。这似乎 100% 有效,但可能只是一个竞争条件!?老实说,我不太确定发生了什么。这个结论可能是错误的,我在这里冒着我的 StackOverflow 点的风险,希望从评论中学习:-P。请务必阅读它们!

I just had the problem as described and found more info here.

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

The statement is: "you can't call an asynchronous method"

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

await asyncmethod2()

from a method that blocks

从一个阻塞的方法

myAsyncMethod().Result

In my case I couldn't change the calling method and it wasn't async. But I didn't actually care about the result. As I remember it also didn't work removing the .Result and have the await missing.

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

So I did this:

所以我这样做了:

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

In my case I didn't care about the result in the calling non-async method but I guess that is quite common in this use case. You can use the result in the calling async method.

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

回答by bozzle

Just a heads up - if you miss the await at the top level in an ASP.NET controller, and you return the task instead of the result as a response, it actually just hangs in the nested await call(s) with no errors. A silly mistake, but had I seen this post it might have saved me some time checking through the code for something odd.

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