我使用Async/Await来释放UI线程并实现多线程。现在我遇到一个问题,当我遇到异常时,我的异步部分的调用堆栈(Call Stack)
总是以ThreadPoolWorkQue.Dipatch()
开头,这对我没有帮助。
我发现了一篇MSDN文章Andrew Stasyuk. Async Causality Chain Tracking,但我理解它并不是一个成熟的解决方案。
如果你使用Async/Await进行多线程编程,最佳/最简单的debug方法是什么?
我使用Async/Await来释放UI线程并实现多线程。现在我遇到一个问题,当我遇到异常时,我的异步部分的调用堆栈(Call Stack)
总是以ThreadPoolWorkQue.Dipatch()
开头,这对我没有帮助。
我发现了一篇MSDN文章Andrew Stasyuk. Async Causality Chain Tracking,但我理解它并不是一个成熟的解决方案。
如果你使用Async/Await进行多线程编程,最佳/最简单的debug方法是什么?
这篇文章很好地解释了为什么调用栈不像我们大多数人想象的那样工作。从技术上讲,调用栈只告诉我们当前方法返回后代码要到哪里。换句话说,调用栈是“代码去哪里”,而不是“代码来自哪里”。
有趣的是,这篇文章在提及解决方案时顺便提到了一个解决方案,但并没有详细解释。我写了一篇博客文章详细介绍了CallContext
解决方案。实质上,您可以使用逻辑调用上下文创建自己的“诊断上下文”。
我喜欢CallContext
解决方案胜过文章中提出的解决方案,因为它适用于所有形式的async
代码(包括类似Task.WhenAll
的fork/join代码)。
这是我所知道的最好的解决方案(除了像钩入分析API这样的复杂操作)。CallContext
方法的注意事项如下:
代码(依赖于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"
{
...
}
}
更新: 我发布了一个NuGet包(在我的博客上有描述),它使用PostSharp自动注入推送和弹出。现在获取良好的跟踪应该会更加简单。
Paralell Stack
这样能提供更多信息的解决方案。但你不能拥有一切 ;-)。如果我想知道自己去过哪里,只有MyStack.Push()
日志记录,我是否正确? - Patrick