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

时间:2014-04-16 23:24:17

标签: c# windows-phone-8 async-await dotnet-httpclient

我的程序遇到了僵局,但我无法弄清楚为什么以及如何解决它。

简短版本:

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

我的主要问题是:

  1. 我遇到了僵局吗?
  2. 我如何验证,也许使用VS,我确实有死锁?
  3. Client.PostAsync吗?如果是,为什么以及如何解决它?
  4. 如果没有,请提出任何意见。

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

1 个答案:

答案 0 :(得分:4)

我相信你看到了僵局,但我不确定如何证明它。

大多数平台上的

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 library。我相信 AsyncContext应该在WP8上运行,但我实际上并没有在该平台上使用它。