2013-04-09 116 views
15

我使用Async/Await来释放我的UI线程并完成多线程。当我遇到异常时,现在遇到问题。我的Async部分的Call Stack总是以ThreadPoolWorkQue.Dipatch()开头,这对我没有多大帮助。调试Async/Await中的异常(调用堆栈)

我发现MSDN文章Andrew Stasyuk. Async Causality Chain Tracking关于它,但据我所知,它不是一个随时可用的解决方案。

如果您在Async/Await中使用多线程,最佳/最简单的调试方法是什么?

回答

18

您发现的文章很好地解释了为什么调用堆栈不能像我们大多数人认为的那样工作。从技术上讲,调用堆栈只告诉我们代码在当前方法之后返回的位置。换句话说,调用堆栈是“代码正在发生的地方”,而不是“代码来自何处”。

有趣的是,文章没有提到通过的解决方案,但没有对其进行阐述。我有a blog post that goes explains the CallContext solution in detail。实质上,您使用逻辑调用上下文来创建您自己的“诊断上下文”。

我喜欢CallContext解决方案比文章,因为它的工作将一切形式的async代码(包括叉/加入如下代码Task.WhenAll)中提出的解决方案更好。

这是我所知道的最好的解决方案(除了做一些非常复杂的事情,像挂钩分析API)。注意事项CallContext方法:

  • 它只适用于.NET 4.5完整。不支持Windows Store应用程序,.NET 4.0等。
  • 您必须手动“测试”您的代码。 AFAIK无法自动注入它。
  • 异常不会自动捕获逻辑调用上下文。因此,如果您在抛出异常时进入调试器,则此解决方案可以正常工作,但如果您只是在其他位置捕获异常并记录它们,那么这种解决方案就不那么有用。

的代码(依赖于immutable collections NuGet library):

public static class MyStack 
{ 
    private static readonly string name = Guid.NewGuid().ToString("N"); 

    private static ImmutableStack<string> CurrentContext 
    { 
     get 
     { 
      var ret = CallContext.LogicalGetData(name) as ImmutableStack<string>; 
      return ret ?? ImmutableStack.Create<string>(); 
     } 

     set 
     { 
      CallContext.LogicalSetData(name, value); 
     } 
    } 

    public static IDisposable Push([CallerMemberName] string context = "") 
    { 
     CurrentContext = CurrentContext.Push(context); 
     return new PopWhenDisposed(); 
    } 

    private static void Pop() 
    { 
     CurrentContext = CurrentContext.Pop(); 
    } 

    private sealed class PopWhenDisposed : IDisposable 
    { 
     private bool disposed; 

     public void Dispose() 
     { 
      if (disposed) 
       return; 
      Pop(); 
      disposed = true; 
     } 
    } 

    // Keep this in your watch window. 
    public static string CurrentStack 
    { 
     get 
     { 
      return string.Join(" ", CurrentContext.Reverse()); 
     } 
    } 
} 

用法:

static async Task SomeWorkAsync() 
{ 
    using (MyStack.Push()) // Pushes "SomeWorkAsync" 
    { 
     ... 
    } 
} 

更新:我发布了使用PostSharp注入推送和自动弹出一个NuGet package (described on my blog)。所以,现在获得一个好的痕迹应该会更简单。

+0

谢谢你的快速回答。我会试着看看它是如何工作的:-)。我以某种方式希望有更多信息将会有像“Paralell Stack”这样的解决方案。但你不能拥有一切;-)。我纠正了只有'MyStack.Push()'记录,如果我想知道我去过哪里,我就需要它吗? – Patrick 2013-04-09 12:55:45

+0

“MyStack.Push”和“MyStack.Pop”(处置)修改堆栈,是的,您需要将它们添加到任何地方。 :(VS2012是第一个支持'async'的版本,希望我们在将来能够得到更好的调试支持! – 2013-04-09 13:13:28

+0

你节省了我的一天,知道从哪里来的realy在调试^^时有所帮助。日子几乎立即变得清晰;它太糟糕了我不能在没有使用你的名字的情况下用google找到你的博客条目也许你可能想要goolge优化,因为你的博客很好,还有其他有用的条目我会在找到时间时阅读。 – Patrick 2013-04-09 14:49:08