调试Async/Await(调用堆栈)中的异常
本文关键字:异常 堆栈 调用 Async Await 调试 | 更新日期: 2023-09-27 18:08:26
我使用Async/Await来释放我的ui线程并完成多线程。现在我遇到一个问题,当我遇到一个异常。我的异步部件的Call Stack
总是以ThreadPoolWorkQue.Dipatch()
开头,这对我帮助不大。
我找到了一篇msdn文章Andrew Stasyuk。异步因果链跟踪,但据我所知,这不是一个现成的解决方案。
什么是最好的/最简单的方法来调试,如果你使用多线程与Async/Await?
您找到的这篇文章很好地解释了为什么调用堆栈并不像我们大多数人认为的那样工作。从技术上讲,调用堆栈只告诉我们代码在当前方法之后返回到哪里。换句话说,调用堆栈是"代码要去哪里",而不是"代码从哪里来"。
有趣的是,这篇文章确实顺便提到了一个解决方案,但没有详细说明。我有一篇博客文章详细解释了CallContext
解决方案。实际上,您可以使用逻辑调用上下文来创建您自己的"诊断上下文"。
我更喜欢CallContext
解决方案,因为它适用于所有形式的async
代码(包括fork/join代码,如Task.WhenAll
)。
这是我所知道的最好的解决方案(除了做一些非常复杂的事情,比如连接到分析API)。CallContext
方法的注意事项:
- 它只在。net 4.5 full上工作。不支持Windows Store应用程序,. net 4.0等 你必须手动"测试"你的代码。AFAIK没有办法自动注入。异常不会自动捕获逻辑调用上下文。因此,如果你在抛出异常时闯入调试器,这个解决方案可以很好地工作,但如果你只是在另一个地方捕获异常并记录它们,它就不那么有用了。
代码(取决于不可变集合NuGet库):
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"
{
...
}
}
更新:我发布了一个NuGet包(在我的博客上描述),它使用PostSharp自动注入推送和弹出。因此,获得一个好的跟踪现在应该简单多了。