异步方法中的异常,调试器无法停止/中断。

73

当一个调试器连接到.NET进程时,通常会在未处理的异常抛出时停止。

然而,当你处于一个async方法中时,这似乎并不起作用。

我之前尝试过的场景列在以下代码中:

class Program
{
    static void Main()
    {
        // Debugger stopps correctly
        Task.Run(() => SyncOp());

        // Debugger doesn't stop
        Task.Run(async () => SyncOp());

        // Debugger doesn't stop
        Task.Run((Func<Task>)AsyncTaskOp);

        // Debugger stops on "Wait()" with "AggregateException"
        Task.Run(() => AsyncTaskOp().Wait());

        // Throws "Exceptions was unhandled by user code" on "await"
        Task.Run(() => AsyncVoidOp());

        Thread.Sleep(2000);
    }

    static void SyncOp()
    {
        throw new Exception("Exception in sync method");
    }

    async static void AsyncVoidOp()
    {
        await AsyncTaskOp();
    }

    async static Task AsyncTaskOp()
    {
        await Task.Delay(300);
        throw new Exception("Exception in async method");
    }
}

我有点不明白,如何让调试器在 AsyncTaskOp() 中的异常处中断/停止?


1
你解决过这个问题吗? - Richard Szalay
@RichardSzalay 不好意思,没有。我想我只是习惯了这种情况。 - Sebastian Krysmanski
你可能想在UserVoice上为我们投票:https://visualstudio.uservoice.com/forums/121579-visual-studio-ide/suggestions/35620360-break-debugger-on-exception-throws-from-async-meth - Liero
此 UserVoice 实例不再可用。 - Toolkit
3个回答

42
在“调试”菜单下选择“异常...”。在“异常”对话框中,在“公共语言运行时异常”一行旁边选中“抛出”复选框。
对于VS2022... 选择“调试”、“窗口”、“异常设置”,然后在“公共语言运行时异常”左侧立即勾选复选框。

23
好的,我知道了。但是如果这样做,调试器会对所有异常进行中断,而不仅仅是未处理的异常,对吗?所以这并不完全是我要找的。 - Sebastian Krysmanski
7
异常已被处理。如果您有一个异步任务方法,那么该异常将被异步状态机捕获并放置在返回的任务上。 - Stephen Cleary
19
在 Visual Studio 2015 中,异常设置可以在 调试(Debug) >> 窗口(Windows) >> 异常设置(Exception Settings) (Ctrl+Alt+E) 下找到。 - Frederik Struck-Schøning
1
@StephenCleary "异常已被处理,但是" - 是的,但它没有被用户代码捕获。即使启用了“只有我的代码”,它仍然认为异步管道内的隐式catch算作用户代码,但实际上不应该这样。 - Dai

3
我想知道是否有人找到了解决这个问题的方法?也许是最新版的Visual Studio中的某个设置...?
一个可行但不太好的解决方案(在我的情况下)是抛出自己的自定义异常,然后修改Stephen Cleary的答案:
在“调试”菜单下选择“异常”(您可以使用此快捷键Control+Alt+E)... 在“异常”对话框中,在“公共语言运行时异常”一行旁边选中“抛出”框。
更具体地说,将您的自定义异常添加到列表中,然后勾选其“抛出”框。
例如:
async static Task AsyncTaskOp()
{
    await Task.Delay(300);
    throw new MyCustomException("Exception in async method");
}

2
如果 Task.Delay() 抛出不同的异常会发生什么? - Thomas Weller
Thomas,我只是在扩展原帖的例子。按照你的逻辑,任何东西都可能失败 - 这是真的,但我们必须在上下文中回答,...所以我们可能会永远围着圈子转...我的答案是针对在代码中停止调试器到一个理想点,然后您可以查看本地变量值和潜在的异常原因。如果异常在任务之外抛出(如OP),则很难实现这一点,因为调试可能会变成一场噩梦。 - Jimbob

-5

我已经在Task.Run(() =>中用try/catch包装了匿名委托。

Task.Run(() => 
{
     try
     {
          SyncOp());
     }
     catch (Exception ex)
     {
          throw;  // <--- Put your debugger break point here. 
                  // You can also add the exception to a common collection of exceptions found inside the threads so you can weed through them for logging
     }

});

4
无济于事,因为这样调试器会在捕获异常的位置中断,而不是抛出异常的位置。 - Sebastian Krysmanski
1
查看实际的异常细节是有帮助的,对于大多数情况来说这已经足够了。 - Nikita

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接