如何验证异步到同步方法和HttpClient.PostAsync调用中的死锁



我的程序出现死锁,但我不知道为什么以及如何修复它。

简称:

asyncsync方法锁定等待异步调用,异步调用调用另一个,依此类推,直到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的线程调试器窗口,但似乎只有主线程处于活动状态。我还尝试了所有其他类型的调试器窗口,但没有任何提示。

我的主要问题是:

  1. 我是不是陷入僵局了
  2. 我如何验证(可能使用VS)我是否存在死锁
  3. 是在Client.PostAsync吗?如果是,为什么以及如何修复
  4. 如果没有,请有什么想法

编辑:方法A使用反射MethodInfo.Invoke调用。我相信现在这是问题的一部分。

我相信你看到了一个死锁,尽管我不知道如何证明它。

大多数平台上的HttpClient在内部正确使用ConfigureAwait(false),但在少数平台上没有。我相信WP8就是其中之一。这意味着您可以随心所欲地正确使用ConfigureAwait(false),但HttpClient仍可能导致死锁。

最好的解决方案是一路异步。但如果这不可能,那么脑海中会出现两个替代方案:

  1. HttpClient工作推送到后台线程。

    public Byte[] A()
    {
      var task = Task.Run(() => BAsync());
      return task.GetAwaiter().GetResult();
    }
    
  2. 在前台线程上建立一个嵌套循环来执行任何异步工作。

    public Byte[] A()
    {
      return AsyncContext.Run(() => BAsync());
    }
    

    请注意,AsyncContext来自我的AsyncEx库。我相信AsyncContext应该在WP8上工作,但我实际上还没有在那个平台上使用过它。

最新更新