我的程序出现死锁,但我不知道为什么以及如何修复它。
简称:
async
到sync
方法锁定等待异步调用,异步调用调用另一个,依此类推,直到HttpClient.PostAsync
调用。http post调用从未发生过,至少它没有到达服务器(我也拥有服务器)。客户端中没有错误。我的主要问题在SO问题的末尾。谢谢
更长版本:
我知道很容易出现僵局,我们必须小心。我正在努力遵守1的建议。将异步向下传播到所有方法和2。尽可能使用ConfigureAwait(false)
。(我认为这一点的主要倡导者是斯蒂芬·克利里,他是我读过的许多SO的博客和答案)。
嗯,我的节目。。。不幸的是,我不能在这里发布完整的示例代码,它相当长;我把它描述为一个同步方法A
,它调用一个异步方法B
,它调用另一个异步,它调用另外一个异步等等。最后一个异步调用是HttpClient.PostAsync
。
程序停止在await HttpClient.PostAsync
。此外,Httppost从不触发(我拥有目标服务器,在这种情况下它从不接收请求)。(我仔细检查了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);
}
应用程序的UI冻结。这是一个WP8应用程序。在这个方面,按钮不对Click
事件作出反应,但ScrollViews
仍然工作。这是另一个问题的主题,但我更愿意在问题中包括这一细节。
我甚至不确定我是否有死锁,或者可能是HttpClient错误?(我不这么认为,同样的方法也适用于同一应用程序中的其他工作流)。
我尝试验证死锁,并使用VS的线程调试器窗口,但似乎只有主线程处于活动状态。我还尝试了所有其他类型的调试器窗口,但没有任何提示。
我的主要问题是:
- 我是不是陷入僵局了
- 我如何验证(可能使用VS)我是否存在死锁
- 是在
Client.PostAsync
吗?如果是,为什么以及如何修复 - 如果没有,请有什么想法
编辑:方法A
使用反射MethodInfo.Invoke
调用。我相信现在这是问题的一部分。
我相信你看到了一个死锁,尽管我不知道如何证明它。
大多数平台上的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库。我相信AsyncContext
应该在WP8上工作,但我实际上还没有在那个平台上使用过它。