调试异步/等待(调用堆栈)中的exception

我使用Async / Await释放我的UI-Thread并完成multithreading。 我遇到exception时遇到问题。 我的异步部分的Call Stack总是从ThreadPoolWorkQue.Dipatch()开始,这对我没有多大帮助。

我找到了MSDN文章Andrew Stasyuk。 Async Causality Chain跟踪它,但据我所知,它不是一个随时可用的解决方案。

如果在Async / Await中使用multithreading,最好/最简单的调试方法是什么?

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

有趣的是,文章确实提到了一个解决方案,但没有阐述它。 我有一篇博文,详细解释了CallContext解决方案 。 实质上,您使用逻辑调用上下文来创建自己的“诊断上下文”。

我喜欢CallContext解决方案比文章中提供的解决方案更好,因为它确实可以工作所有forms的async代码(包括像Task.WhenAll这样的fork / join代码)。

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

  • 它仅适用于.NET 4.5完整版。 不支持Windowsapp store应用,.NET 4.0等。
  • 您必须手动“检测”您的代码。 AFAIK无法自动注入它。
  • exception不会自动捕获逻辑调用上下文。 因此,如果您在抛出exception时闯入调试器,此解决方案可以正常工作,但如果您只是在另一个地方捕获exception并记录它们,那么它就不那么有用了。

代码(取决于不可变集合NuGet库 ):

 public static class MyStack { private static readonly string name = Guid.NewGuid().ToString("N"); private static ImmutableStack CurrentContext { get { var ret = CallContext.LogicalGetData(name) as ImmutableStack; return ret ?? ImmutableStack.Create(); } 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" { ... } } 

更新:我发布了一个NuGet包(在我的博客上描述) ,它使用PostSharp自动注入推送和弹出。 因此,现在获得良好的追踪应该更加简单。