jba*_*eda 0 c# async-await dotnet-httpclient windows-phone-8
我的程序出现死锁,但我不知道为什么以及如何解决它。
精简版:
一个asynctosync方法锁定等待异步调用,它调用另一个,依此类推,直到HttpClient.PostAsync调用。http post 调用永远不会发生,至少它没有到达服务器(我也拥有)。客户端没有错误。我的主要问题是在这个 SO 问题的末尾。谢谢!
更长的版本:
我知道很容易出现死锁,我们必须小心。我试图坚持 1. 将异步传播到所有方法和 2.ConfigureAwait(false)尽可能使用/有意义的建议。(我认为这一点的主要倡导者是 Stephen Cleary 作为他的博客和我阅读过的许多 SO 中的答案)。
好吧,我的程序……不幸的是,我不能在这里发布完整的示例代码,它很长;我把它描述为一个同步方法A,它调用一个异步方法B,它调用另一个异步,它调用另一个异步,等等。最终最后一个异步调用是HttpClient.PostAsync.
程序停止在await HttpClient.PostAsync。此外,Http post 永远不会触发(我拥有目标服务器,在这种情况下它永远不会收到请求)。(我仔细检查了 uri 等)。
因此,同步方法会锁定等待B的异步任务完成,而这从未发生过。A看起来像这样:
public Byte[] A()
{
var task = BAsync();
return task.GetAwaiter().GetResult();
}
Run Code Online (Sandbox Code Playgroud)
我以这种方式使用同步,因为他的方法不需要返回上下文,我看到斯蒂芬在 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);
}
Run Code Online (Sandbox Code Playgroud)
应用程序的 UI 冻结。这是一个 WP8 应用程序。在这方面,按钮不会对Click事件做出反应,但ScrollViews仍然有效。这是另一个问题的主题,但我更喜欢在问题中包含这个细节。
我什至不确定我有死锁,还是 HttpClient 错误?(我不这么认为,同样的方法在同一个应用程序的其他工作流程中也有效)。
我试图验证死锁并使用 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();
}
Run Code Online (Sandbox Code Playgroud)在前台线程上建立嵌套循环以执行任何异步工作。
public Byte[] A()
{
return AsyncContext.Run(() => BAsync());
}
Run Code Online (Sandbox Code Playgroud)
请注意,这AsyncContext是来自我的AsyncEx 库。我相信 AsyncContext应该可以在 WP8 上工作,但我还没有在该平台上实际使用过它。