我的程序遇到了僵局,但我无法弄清楚为什么以及如何解决它。
简短版本:
async
到sync
方法锁定等待异步调用,调用另一个异步调用,依此类推,直到HttpClient.PostAsync
调用。 http post调用永远不会发生,至少它不会到达服务器(我也拥有)。客户端没有错误。我的主要问题是在这个问题的最后。谢谢!
更长的版本:
我知道解决僵局很容易,我们必须小心。我正在努力遵守以下建议:1.将异步传播到所有方法,2。尽可能使用ConfigureAwait(false)
/有意义。 (我认为主要的提倡者是Stephen Cleary作为他的博客和许多SO的答案,我已经阅读了很多这些内容)。
好吧,我的节目......
我不能在这里发布完整的示例代码,它相当长;我将其描述为同步方法A
,它调用异步方法B
,该方法调用另一个异步,调用另一个异步,等等。
最后,最后一次异步调用是HttpClient.PostAsync
。
程序停在await HttpClient.PostAsync
处。此外,Http帖子永远不会触发(我拥有目标服务器,在这种情况下它永远不会收到请求)。 (我仔细检查了uri等。)。
因此,同步方法锁定等待B
的异步任务完成,从而永远不会发生。 A
看起来像这样:
public Byte[] A()
{
var task = BAsync();
return task.GetAwaiter().GetResult();
}
我以这种方式使用同步,因为他的方法不需要上下文,我看到Stephen在Nito中建议的方法是使用这个来实现的。 (我也试过了task.Result
而不是相同的结果)。
http post电话的代码是:
private static async Task<HttpResponseMessage> PostAsync(Uri serverUri, HttpContent httpContent)
{
var client = new HttpClient();
return await client.PostAsync(serverUri, httpContent).ConfigureAwait(false);
}
应用程序的用户界面冻结。这是一个WP8应用程序。在这方面,按钮不会对Click
事件做出反应,但ScrollViews
仍然有效。这是另一个问题的主题,但我更愿意在问题中包含这个细节。
我甚至不确定我是否有死锁,或者可能是HttpClient错误? (我不这么认为,同一个应用程序中的其他工作流程也使用相同的方法)。
我试图验证死锁并使用VS的线程调试器窗口,但只有主线程似乎是活动的。我也试过所有其他类型的调试器窗口,但没有给我一个提示。
我的主要问题是:
Client.PostAsync
吗?如果是,为什么以及如何解决它?修改:使用A
的反射调用方法MethodInfo.Invoke
。我相信现在这是问题的一部分。
答案 0 :(得分:4)
我相信你看到了僵局,但我不确定如何证明它。
大多数平台上的 HttpClient
在内部正确使用ConfigureAwait(false)
,但在少数平台上则没有。我相信WP8是其中之一。这意味着您可以正确使用ConfigureAwait(false)
,但HttpClient
仍然可能导致死锁。
最好的解决方案是始终保持异步。但如果那是不可能的,那么我会想到两种选择:
将HttpClient
工作推送到后台线程。
public Byte[] A()
{
var task = Task.Run(() => BAsync());
return task.GetAwaiter().GetResult();
}
在前台线程上建立一个嵌套循环来执行任何异步工作。
public Byte[] A()
{
return AsyncContext.Run(() => BAsync());
}
请注意,AsyncContext
来自我的AsyncEx library。我相信 AsyncContext
应该在WP8上运行,但我实际上并没有在该平台上使用它。