gpt4 book ai didi

c# - 如何仅在 ContinueWith 中出现未处理的异常时中断?

转载 作者:太空宇宙 更新时间:2023-11-03 13:09:57 32 4
gpt4 key购买 nike

当我编写基于任务的代码时,我的一些 ContinueWith 子句有意抛出异常(我捕获并适当处理),而其中一些意外抛出异常(由于错误)。我怎样才能避免打破第一种,同时仍然打破第二种?

在下面的代码中,我希望调试器在无意异常时中断,而不是在有意异常时中断(因为稍后会处理)。如果我根据 this question 禁用“Just My Code” ,则有意异常不会破坏调试器(正确),但无意异常也不会破坏调试器(不正确)。如果我启用“仅我的代码”,则无意异常确实会破坏调试器(正确),但有意异常也会如此(不正确)。

是否有任何设置可以使 ContinueWith 子句中的异常像普通开发人员可能期望的那样工作?

class Program
{
static void Main(string[] args)
{
//Intentional Exception
Task.Factory
.StartNew(() => Console.WriteLine("Basic action"))
.ContinueWith((t1) => { throw new Exception("Intentional Exception"); })
.ContinueWith((t2) => Console.WriteLine("Caught '" + t2.Exception.InnerException.Message + "'"));

//Unintentional Exception
Task.Factory
.StartNew(() => Console.WriteLine("Basic action"))
.ContinueWith((t3) => { throw new Exception("Unintentional Exception (bug)"); });

Console.ReadLine();
}
}

最佳答案

有两种可能的方法来解决这个问题。两者都不是很令人满意,因为它们基本上依赖于开发人员检测 100% 自己的错误,这实际上是调试器在开发环境中应该做的事情。对于任何一种方法,开发人员都应首先关闭“仅我的代码”,因为这是防止捕获/观察到的故意异常导致调试器中断的方法。

首先,正如@Peter Deniho 在 his answer 中提到的那样及其注释,开发人员可以监听 TaskScheduler.UnobservedTaskException 事件。代码很简单:

TaskScheduler.UnobservedTaskException += (sender, e) =>
{
throw new Exception("An Exception occurred in a Task but was not observed", e.Exception.InnerException);
};

这种方法的困难在于,只有在调用故障任务的终结器时才会触发此事件。这发生在垃圾回收期间,但垃圾回收可能要到异常发生很久之后才会发生。要看出这是一个严重的问题,请使用本段后面提到的方法,但注释掉 GC.Collect()。在那种情况下,在抛出未观察到的异常之前,心跳将持续很长时间。为了解决这个问题,必须尽可能频繁地强制执行垃圾收集,以确保将未观察到/无意的异常报告给开发人员。这可以通过定义以下方法并在原始 static void Main 的开头调用它来实现:

private static async void WatchForUnobservedExceptions()
{
TaskScheduler.UnobservedTaskException += (sender, e) =>
{
throw new Exception("An Exception occurred in a Task but was not observed", e.Exception.InnerException);
};

//This polling loop is necessary to ensure the faulted Task's finalizer is called (which causes UnobservedTaskException to fire) in a timely fashion
while (true)
{
Console.WriteLine("Heartbeat");
GC.Collect();
await Task.Delay(5000);
}
}

当然,这具有严重的性能影响,而且这是必要的事实是 pretty good indicator of fundamentally broken code .

上述方法的特点是定期轮询以自动检查可能遗漏的任务异常。第二种方法是显式检查每个任务的异常。这意味着开发人员必须识别任何未观察到异常(通过 await、Wait()、Result 或 Exception)的“后台”任务,并对任何未观察到的异常进行处理。这是我首选的扩展方法:

static class Extensions
{
public static void CrashOnException(this Task task)
{
task.ContinueWith((t) =>
{
Console.WriteLine(t.Exception.InnerException);
Debugger.Break();
Console.WriteLine("The process encountered an unhandled Exception during Task execution. See above trace for details.");
Environment.Exit(-1);
}, TaskContinuationOptions.OnlyOnFaulted);
}
}

一旦定义,这个方法可以简单地附加到任何任务,否则如果它有错误可能有未观察到的异常:

Task.Factory
.StartNew(() => Console.WriteLine("Basic action"))
.ContinueWith((t3) => { throw new Exception("Unintentional Exception (bug)"); })
.CrashOnException();

这种方法的主要缺点是必须确定每个适当的任务(大多数任务不需要或不需要这种修改,因为大多数任务会在程序中的某个 future 点使用),然后由开发人员添加后缀。如果开发者忘记给一个 Task 加上 unobserved Exception 的后缀,它就会像原题中 Unintentional Exception 的行为一样被默默地吞掉。这意味着这种方法由于粗心大意而无法有效地捕获错误,即使这样做就是这个问题的内容。为了降低发现错误的可能性,您不会受到第一种方法的性能影响。

关于c# - 如何仅在 ContinueWith 中出现未处理的异常时中断?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29359003/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com