Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 682|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-6-11 13:28:24 | 显示全部楼层 |阅读模式

     

    异常处理之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处理的。

     

     补充说明:

    AppDomain.CurrentDomain.FirstChanceException事件会在First Chance时触发。保留部分First Chance有助于排查某些复杂的问题。我通常会保存最近十条First Chance异常,程序彻底崩溃时输出到log。
    AppDomain.CurrentDomain.UnhandledException事件会在未捕获的异常抛出时触发。这个时候你的程序基本上挂掉了,所以要输出到log。
    对于WPF程序,Application.Current.DispatcherUnhandledException会在Dispatcher中未捕获的异常抛出时触发。通常这个时候你的程序已经要挂了,也要输出到log。

     

    出处:https://www.cnblogs.com/luminji/archive/2011/01/05/1926033.html

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-23 07:09 , Processed in 0.061321 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表