我使用Async / Await释放我的UI-Thread并完成多线程。我遇到异常时遇到问题。我的异步部分的Call Stack
始终以ThreadPoolWorkQue.Dipatch()
开头,这对我没什么帮助。
我找到了关于它的MSDN文章Andrew Stasyuk. Async Causality Chain Tracking,但据我了解,它不是一个现成的解决方案。
如果在Async / Await中使用多线程,最好/最简单的调试方法是什么?
答案 0 :(得分:20)
您找到的文章很好地解释了为什么调用堆栈不像我们大多数人认为的那样工作。从技术上讲,调用堆栈只告诉我们在当前方法之后代码返回的位置。换句话说,调用堆栈是“代码的去向”,而不是“代码来自哪里”。
有趣的是,文章确实提到了一个解决方案,但没有说明。我有a blog post that goes explains the CallContext
solution in detail。实质上,您使用逻辑调用上下文来创建自己的“诊断上下文”。
我更喜欢CallContext
解决方案,而不是文章中提供的解决方案,因为它确实可以用于所有形式的async
代码(包括叉/加密代码,如Task.WhenAll
)。
这是我所知道的最佳解决方案(除了做一些非常复杂的事情,比如挂钩到分析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 package (described on my blog),它使用PostSharp自动注入推送和弹出。因此,现在获得良好的追踪应该更加简单。