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

时间:2013-04-09 09:32:22

标签: c# stack-trace async-await visual-studio-debugging c#-5.0

我使用Async / Await释放我的UI-Thread并完成多线程。我遇到异常时遇到问题。我的异步部分的Call Stack始终以ThreadPoolWorkQue.Dipatch()开头,这对我没什么帮助。

我找到了关于它的MSDN文章Andrew Stasyuk. Async Causality Chain Tracking,但据我了解,它不是一个现成的解决方案。

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

1 个答案:

答案 0 :(得分:20)

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

有趣的是,文章确实提到了一个解决方案,但没有说明。我有a blog post that goes explains the CallContext solution in detail。实质上,您使用逻辑调用上下文来创建自己的“诊断上下文”。

我更喜欢CallContext解决方案,而不是文章中提供的解决方案,因为它确实可以用于所有形式的async代码(包括叉/加密代码,如Task.WhenAll)。

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

  • 它仅适用于.NET 4.5。不支持Windows应用商店应用,.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"
    {
        ...
    }
}

更新:我发布了NuGet package (described on my blog),它使用PostSharp自动注入推送和弹出。因此,现在获得良好的追踪应该更加简单。

相关问题