Winform異常處理之ThreadException、unhandledException及多線程異常處理

 

異常處理之ThreadException、unhandledException及多線程異常處理html

 

一:ThreadException和unhandledException的區別 windows

處理未捕獲的異常是每一個應用程序起碼有的功能,C#在AppDomain提供了UnhandledException 事件來接收未捕獲到的異常的通知。常見的應用以下:   api

複製代碼
代碼
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能夠阻止應用程序終止。具體使用方法以下:  app

複製代碼
代碼
[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。代碼以下:   dom

複製代碼
代碼
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窗體程序中,若是在窗體線程中,       ide

throw new Exception( " 窗體線程異常 " );

將會觸發ThreadException事件。  post

Thread t = new Thread((ThreadStart) delegate
{
throw new Exception( " 非窗體線程異常 " );
});
t.Start();

 將會觸發UnhandledException事件,而後整個應用程序會被終止。this

 

二:多線程異常處理spa

 

多線程的異常處理,要採用特殊的作法。如下的處理方式會存在問題:   

複製代碼
代碼
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

相關文章
相關標籤/搜索