我已在测试应用中应用这些处理程序:
Application.ThreadException += Application_ThreadException;
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
然后我在窗体上的嵌套Task / Async await上创建一个异常:
尽管处理程序被解雇 - CurrentDomain.UnhandledException
- 该应用程序仍然崩溃。 击>
为什么会崩溃而不显示对话框并继续运行?
System.Exception未处理
消息:mscorlib.dll中出现未处理的“System.Exception”类型异常
其他信息:dd
private async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("Main " + Thread.CurrentThread.ManagedThreadId);
try
{
await Hello();
}
catch (Exception ex)
{
Console.WriteLine("Exception on main " + Thread.CurrentThread.ManagedThreadId);
}
}
private async static Task Hello() //changed from void
{
await Task.Run(() => new IGetRun().Run1());
}
internal class IGetRun
{
public async Task Run1() //changed from void
{
Console.WriteLine("Run1 " + Thread.CurrentThread.ManagedThreadId);
await Task.Run(() => new IGetRun2().Run2());
}
}
internal class IGetRun2
{
public void Run2()
{
Console.WriteLine("Run2 " + Thread.CurrentThread.ManagedThreadId);
throw new Exception("dd");
}
}
修改
看起来我忘了使用Task声明每个异步方法而不是void,因此异常处理现在可以预测。我还不知道的唯一原因是 - 如果我在按钮事件中停止处理异常 - 当捕获异常时Application_ThreadException
被触发而不是TaskScheduler_UnobservedTaskException
答案 0 :(得分:7)
您的原始代码会执行您不应该执行的操作:调用async void
方法。有问题的异常处理是其中一个原因。
让我们一个一个地看一下事件处理程序:
Application.ThreadException
仅处理Winforms UI线程上的异常。异常从未进入UI线程,因此此事件不会触发。TaskScheduler.UnobservedTaskException
仅处理来自Task
的未被观察到的异常(即使这样,它们在最终确定时会这样做,如果您没有分配大量内存,则可能需要很长时间)。但是,未观察到的异常与任何Task
都没有关联,所以这也不会触发。AppDomain.CurrentDomain.UnhandledException
处理在前两个事件处理程序之后仍被视为未被观察到的所有异常。但它不能用于设置观察到的异常,因此应用程序仍然会终止。这是实际触发的唯一处理程序。为什么前两个处理程序确实没有开火?在您的代码中,您有Task.Run(() => new IGetRun().Run1())
,其中Run1()
是引发异常的async void
方法。
当async void
方法中存在未观察到的异常时,将在当前同步上下文中重新抛出异常。但由于该方法在线程池线程上运行(因为Task.Run()
),因此没有同步上下文。因此,在线程池线程上抛出异常,前两个事件处理程序无法观察到该异常。
正如您已经发现的,此问题的解决方法是使用async Task
方法并正确使用await
。
在更新的代码中,Run1()
现在是async Task
方法。由Task
然后Task.Run()
解包await
,因此异常将转移到Hello()
Task
。反过来,它是来自UI线程上的await
处理程序的async void button1_Click
。
这意味着没有未观察到的Task
异常,并且在UI同步上下文中重新抛出异常。在Winforms中,这意味着Application.ThreadException
被引发,这正是您所观察到的。
答案 1 :(得分:0)
我不确定你是否解决了问题? 如果要抓住你的unhandeld例外,你可以这样做。 在Application.Run
之前添加到program.csApplication.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
创建2个空格来处理异常数据。
static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
MessageBox.Show(e.Exception.Message, "Unhandled Thread Exception");
// here you can log the exception ...
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
MessageBox.Show((e.ExceptionObject as Exception).Message, "Unhandled UI Exception");
// here you can log the exception ...
}