异常处理
任何线程创建范围内try/catch/finally块,当线程开始执行便不再与其有任何关系。考虑下面的程序:
public static void Main() { try { new Thread (Go).Start(); } catch (Exception ex) { // 不会在这得到异常 Console.WriteLine ("Exception!"); } static void Go() { throw null; } }
这里try/catch语句一点用也没有,新创建的线程将引发NullReferenceException异常。当你考虑到每个线程有独立的执行路径的时候,便知道这行为是有道理的,补救方法是在线程处理的方法内加入他们自己的异常处理:
public static void Main() { new Thread (Go).Start(); } static void Go() { try { ... throw null; // 这个异常在下面会被捕捉到 ... } catch (Exception ex) { 记录异常日志,并且或通知另一个线程 我们发生错误 ... }
从.NET 2.0开始,任何线程内的未处理的异常都将导致整个程序关闭,这意味着忽略异常不再是一个选项了。因此为了避免由未处理异常引起的程序崩溃,try/catch块需要出现在每个线程进入的方法内,至少要在产品程序中应该如此。对于经常使用“全局”异常处理的Windows Forms程序员来说,这可能有点麻烦,像下面这样:
using System; using System.Threading; using System.Windows.Forms; static class Program { static void Main() { Application.ThreadException += HandleError; Application.Run (new MainForm()); } static void HandleError (object sender, ThreadExceptionEventArgs e) { 记录异常或者退出程序或者继续运行... } }
Application.ThreadException事件在异常被抛出时触发,以一个Windows信息(比如:键盘,鼠标活着 “paint” 等信息)的方式,简言之,一个Windows Forms程序的几乎所有代码。虽然这看起来很完美,它使人产生一种虚假的安全感——所有的异常都被中央异常处理捕捉到了。由工作线程抛出的异常便是一个没有被Application.ThreadException捕捉到的很好的例外。(在Main方法中的代码,包括构造器的形式,在Windows信息开始前先执行)
.NET framework为全局异常处理提供了一个更低级别的事件:AppDomain.UnhandledException,这个事件在任何类型的程序(有或没有用户界面)的任何线程有任何未处理的异常触发。尽管它提供了好的不得已的异常处理解决机制,但是这不意味着这能保证程序不崩溃,也不意味着能取消.NET异常对话框。