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

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

一:ThreadException和unhandledException的区别

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

staticvoid Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException +=new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}

staticvoid 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]
staticvoid Main()
{
	Application.ThreadException +=new ThreadExceptionEventHandler(UIThreadException);
	Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
	AppDomain.CurrentDomain.UnhandledException +=
	new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
	Application.Run(new ErrorHandlerForm());
}

privatestaticvoid 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窗体异常,应用程序将退出!");
	}
}

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

privatevoid 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窗体程序中,如果在窗体线程中,
thrownew Exception(“窗体线程异常”);

将会触发ThreadException事件。

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

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

二:多线程异常处理

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

try
{
	Thread t =new Thread((ThreadStart)delegate
	{
		thrownew 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
	{
		thrownew Exception("多线程异常");
	}
	catch (Exception error)
	{
		MessageBox.Show("工作线程异常:"+ error.Message + Environment.NewLine + error.StackTrace);
	}
});
t.Start();

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

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

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

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

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

Thread t =new Thread((ThreadStart)delegate
{
	try
	{
		thrownew 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处理的。

转自:https://www.cnblogs.com/luminji/archive/2011/01/05/1926033.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用 System.Windows.Forms.Application.Restart() 方法来实现 WinForm 应用程序的自动重启。这个方法会关闭当前应用程序的所有进程,并重新启动应用程序。 以下是一个示例代码: ```csharp using System; using System.Windows.Forms; public class Program { public static void Main() { Application.SetUnhandledExceptionMode(UnhandledExceptionMode.Automatic); // 设置未处理异常的模式为自动模式 Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException); // 处理应用程序线程的未处理异常 // 启动主窗体 Application.Run(new MainForm()); } private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) { // 发生未处理异常时,可以在此处进行处理或记录日志 // 重启应用程序 Application.Restart(); } } public class MainForm : Form { // 主窗体的代码 } ``` 在这个示例中,我们在 `Main` 方法中设置了应用程序的未处理异常模式为自动模式,并通过订阅 `Application.ThreadException` 事件来处理应用程序线程的未处理异常。在 `Application_ThreadException` 方法中,你可以添加你想要的异常处理逻辑,然后调用 `Application.Restart()` 方法来重启应用程序。 请注意,使用 `Application.Restart()` 方法时,当前应用程序的所有进程都会被关闭,然后重新启动。因此,请确保在调用该方法之前保存用户数据和状态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值