zoukankan      html  css  js  c++  java
  • 转:异常处理之ThreadException、unhandledException及多线程异常处理

    转载自:http://www.cnblogs.com/levin9/articles/2319251.html

    一: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窗体线程异常 : 
    
    "; 
           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 = "非窗体线程异常 : 
    
    ";    
            MessageBox.Show(errorMsg + ex.Message + Environment.NewLine + ex.StackTrace);   
        }
        catch 
        {         
            MessageBox.Show("不可恢复的非Windows窗体线程异常,应用程序将退出!");  
        }   
    }

    除了Windows窗体程序,再来说一下WPF程序。WPF的UI线程和Windows的UI线程有点不一样。WPF的UI线程是交给一个叫做调度器的类: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窗体线程异常 : "; 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窗体线程异常 : "; 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处理的。

  • 相关阅读:
    mysq 日期相减
    说说时间观与时间管理——北漂18年(71)
    ionic之切换开关
    ionic之单选框
    SELECT ... LOCK IN SHARE MODE和SELECT ... FOR UPDATE locks在RR模式下可以看到最新的记录
    14.5.2.3 Consistent Nonlocking Reads 一致性非锁定读
    14.5.2.2 autocommit, Commit, and Rollback
    14.5.2 事务隔离级别
    对于唯一索引使用唯一条件搜索, InnoDB 只锁定找到的index record,不是它之前的区间
    mysql explain 解释
  • 原文地址:https://www.cnblogs.com/lusunqing/p/3197951.html
Copyright © 2011-2022 走看看