异常处理之ThreadException、unhandledException及多线程异常处理

简介:

异常处理之ThreadException、unhandledException及多线程异常处理

 

一:ThreadException和unhandledException的区别

处理未捕获的异常是每个应用程序起码有的功能,C#在AppDomain提供了UnhandledException 事件来接收未捕获到的异常的通知。常见的应用如下:   

复制代码
代码
static void  Main( string [] args)
{
AppDomain.CurrentDomain.UnhandledException 
+= new  UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}

static void  CurrentDomain_UnhandledException( object  sender, UnhandledExceptionEventArgs e)
{
Exception error 
=  (Exception)e.ExceptionObject;
Console.WriteLine(
" MyHandler caught :  " +  error.Message);
}
复制代码

未捕获的异常,通常就是运行时期的BUG,于是我们可以在UnhandledException 的注册事件方法CurrentDomain_UnhandledException中将未捕获异常的信息记录在日志中。值得注意的是,UnhandledException提供的机制并不能阻止应用程序终止,也就是说,CurrentDomain_UnhandledException方法执行后,应用程序就会被终止。

上面我们举的例子来自于控制台程序,UnhandledException可以在任何应用程序域中使用,在某些应用程序模型,如windows窗体程序,还存在ThreadException来处理 Windows 窗体线程中所发生的其未经处理的异常。即,在windows窗体程序中,使用ThreadException 事件来处理 UI 线程异常,使用 UnhandledException 事件来处理非 UI 线程异常。ThreadException可以阻止应用程序终止。具体使用方法如下:  

复制代码
代码
[STAThread]
static void  Main()
{
Application.ThreadException 
+= new  ThreadExceptionEventHandler(UIThreadException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException 
+=
new  UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.Run(
new  ErrorHandlerForm());
}

private static void  UIThreadException( object  sender, ThreadExceptionEventArgs t)
{
try
{
string  errorMsg  = " Windows窗体线程异常 : \n\n " ;
MessageBox.Show(errorMsg 
+  t.Exception.Message  +  Environment.NewLine  +  t.Exception.StackTrace);
}
catch
{
MessageBox.Show(
" 不可恢复的Windows窗体异常,应用程序将退出! " );
}
}

private static void  CurrentDomain_UnhandledException( object  sender, UnhandledExceptionEventArgs e)
{
try
{
Exception ex 
=  (Exception)e.ExceptionObject;
string  errorMsg  = " 非窗体线程异常 : \n\n " ;
MessageBox.Show(errorMsg 
+  ex.Message  +  Environment.NewLine  +  ex.StackTrace);
}
catch
{
MessageBox.Show(
" 不可恢复的非Windows窗体线程异常,应用程序将退出! " );
}
}
复制代码

 除了Windows窗体程序,再来说一下WPF程序。WPFUI线程和WindowsUI线程有点不一样。WPFUI线程是交给一个叫做调度器的类:Dispatcher。代码如下:   

复制代码
代码
public  App()
{
this .DispatcherUnhandledException  += new  DispatcherUnhandledExceptionEventHandler(Application_DispatcherUnhandledException);
AppDomain.CurrentDomain.UnhandledException 
+= new  UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}

void  CurrentDomain_UnhandledException( object  sender, UnhandledExceptionEventArgs e)
{
try
{
Exception ex 
=  e.ExceptionObject  as  Exception;
string  errorMsg  = " 非WPF窗体线程异常 : \n\n " ;
MessageBox.Show(errorMsg 
+  ex.Message  +  Environment.NewLine  +  ex.StackTrace);
}
catch
{
MessageBox.Show(
" 不可恢复的WPF窗体线程异常,应用程序将退出! " );
}
}

private void  Application_DispatcherUnhandledException( object  sender, DispatcherUnhandledExceptionEventArgs e)
{
try
{
Exception ex 
=  e.Exception;
string  errorMsg  = " WPF窗体线程异常 : \n\n " ;
MessageBox.Show(errorMsg 
+  ex.Message  +  Environment.NewLine  +  ex.StackTrace);
}
catch
{
MessageBox.Show(
" 不可恢复的WPF窗体线程异常,应用程序将退出! " );
}
}
复制代码

无论是Windows窗体程序还是WPF程序,我们都看到捕获的异常当中分为"窗体线程异常"和"非窗体线程异常"。如在Windows窗体程序中,如果在窗体线程中,       

throw new  Exception( " 窗体线程异常 " );

将会触发ThreadException事件。  

Thread t  = new  Thread((ThreadStart) delegate
{
throw new  Exception( " 非窗体线程异常 " );
});
t.Start();

 将会触发UnhandledException事件,然后整个应用程序会被终止。

 

二:多线程异常处理

 

多线程的异常处理,要采用特殊的做法。以下的处理方式会存在问题:   

复制代码
代码
try
{
Thread t 
= new  Thread((ThreadStart) delegate
{
throw new  Exception( " 多线程异常 " );
});
t.Start();
}
catch  (Exception error)
{
MessageBox.Show(error.Message 
+  Environment.NewLine  +  error.StackTrace);
}
复制代码

 应用程序并不会在这里捕获线程t中的异常,而是会直接退出。从.NET2.0开始,任何线程上未处理的异常,都会导致应用程序的退出(先会触发AppDomain的UnhandledException)。上面代码中的try-catch实际上捕获的还是当前线程的异常,而t是属于新起的异常,所以,正确的做法应该是:  

复制代码
代码
Thread t  = new  Thread((ThreadStart) delegate
{
try
{
throw new  Exception( " 多线程异常 " );
}
catch  (Exception error)
{
MessageBox.Show(
" 工作线程异常: " +  error.Message  +  Environment.NewLine  +  error.StackTrace);
}
});
t.Start();
复制代码

 也就是说,新起的线程中异常的捕获,可以将线程内部代码全部try起来。原则上来说,每个线程自己的异常应该在自己的内部处理完毕,不过仍旧有一个办法,可以将线程内部的异常传递到主线程。

在Windows窗体程序中,可以使用窗体的BeginInvoke方法来将异常传递给主窗体线程:  

复制代码
代码
Thread t  = new  Thread((ThreadStart) delegate
{
try
{
throw new  Exception( " 非窗体线程异常 " );
}
catch  (Exception ex)
{
this .BeginInvoke((Action) delegate
{
throw  ex;
});
}
});
t.Start();
复制代码

上文的代码将最终引发主线程的Application.ThreadException。最终的结果看起来有点像:  

 

WPF窗体程序中,你可以采用如下的方法将工作线程的异常传递到主线程:  

复制代码
代码
Thread t  = new  Thread((ThreadStart) delegate
{
try
{
throw new  Exception( " 非窗体线程异常 " );
}
catch  (Exception ex)
{
this .Dispatcher.Invoke((Action) delegate
{
throw  ex;
}
);
}
});
t.Start();
复制代码

WPF窗体程序的处理方式与Windows窗体程序比较,有两个很有意思的地方:

第一个是,在Windows窗体中,我们采用的是BeginInvoke方法。你会发现使用Invoke方法,并不能引发主线程的Application.ThreadException。而在WPF窗体程序中,无论是调度器的Invoke还是BeginInvoke方法都能将异常传递给主线程。

第二个地方就是InnerException。WPF的工作线程异常将会抛到主线程,变成主线程异常的InnerException,而Windows窗体程序的工作线程异常,将会被吃掉,直接变为null,只是在异常的Message信息中保存工作线程异常的Message。

 

三:ASP.NET异常处理

我们都知道ASP.NET的全局异常处理方法是Global中的Application_Error方法。我曾经查过ASP.NET的Appdomain.CurrentDomain.unhandledException,结果用反射得到的结果,unhandledException所注册的事件方法根本不是这个方法。联想到ASP.NET页面,包括这个全局处理类,都是交给aspnet_isapi.dll处理的,而aspnet_isapi.dll不是一个托管程序集。所以,应该理解为,ASP.NET的未捕获异常的处理,不同于托管异常(即CLR异常),而是交给aspnet_isapi.dll这个非托管DLL处理的。


本文转自最课程陆敏技博客园博客,原文链接:http://www.cnblogs.com/luminji/archive/2011/01/05/1926033.html,如需转载请自行联系原作者

相关文章
|
4月前
|
Java
java 多线程异常处理
本文介绍了Java中ThreadGroup的异常处理机制,重点讲解UncaughtExceptionHandler的使用。通过示例代码展示了当线程的run()方法抛出未捕获异常时,JVM如何依次查找并调用线程的异常处理器、线程组的uncaughtException方法或默认异常处理器。文章还提供了具体代码和输出结果,帮助理解不同处理器的优先级与执行逻辑。
121 1
|
监控 Java
解析Java线程池的异常处理机制
该内容是一个关于Java线程和线程池异常处理的总结。提到的关键点包括: 1. 引用了滑动验证页面和相关文章资源。 2. 区分了`execute`与`submit`在处理线程异常时的区别,`submit`可能会捕获并隐藏异常,而`execute`会直接抛出。 3. 提供了处理线程和线程池异常的建议,如使用try/catch直接捕获,或者自定义线程工厂和未捕获异常处理器。 4. 示例代码展示了如何通过设置`UncaughtExceptionHandler`来监控和处理线程中的异常。 请注意,由于字符限制,这里只提供了简要摘要,详细解释和代码示例请参考原文。
224 3
|
网络协议 安全 Python
我们将使用Python的内置库`http.server`来创建一个简单的Web服务器。虽然这个示例相对简单,但我们可以围绕它展开许多讨论,包括HTTP协议、网络编程、异常处理、多线程等。
我们将使用Python的内置库`http.server`来创建一个简单的Web服务器。虽然这个示例相对简单,但我们可以围绕它展开许多讨论,包括HTTP协议、网络编程、异常处理、多线程等。
|
Java
JAVA难点包括异常处理、多线程、泛型和反射,以及复杂的分布式系统知识
【5月更文挑战第2天】JAVA难点包括异常处理、多线程、泛型和反射,以及复杂的分布式系统知识。入坑JAVA因它的面向对象特性、平台无关性、强大的标准库和活跃的社区支持。
174 2
|
Java
【Java技术指南】「技术盲区」看看线程以及线程池的异常处理机制都有哪些?
【Java技术指南】「技术盲区」看看线程以及线程池的异常处理机制都有哪些?
379 0
|
监控 调度 Ruby
【Ruby高级技术】在项目中使用多线程之后的一系列问题解决方案-同步控制、异常处理、死锁处理
【Ruby高级技术】在项目中使用多线程之后的一系列问题解决方案-同步控制、异常处理、死锁处理
277 0
|
Java
Java线程未捕获异常处理 UncaughtExceptionHandler
当一个线程在执行过程中抛出了异常,并且没有进行try..catch,那么这个线程就会终止运行。
274 0
|
消息中间件 缓存 前端开发
多线程异常处理:挖掘页面空窗背后的原因
作为一名应用开发,大家是否有遇到以下现象,为什么一套非常优秀的兜底机制还是会出现页面空窗现象?本文将会通过实例和大家分享,作者在线程池使用过程中遇到的问题:异常处理,以及下线程池的参数设置经验。
多线程异常处理:挖掘页面空窗背后的原因