2008-10-09 105 views

回答

35

首先,你真的應該儘量避免在後臺線程中拋出異常並且不處理異常。如果你控制你的委託經營的方式,將其封裝在一個try catch塊和(使用EndInvoke會,如果你顯式調用的BeginInvoke,或更新某個地方某個共享狀態)計算的方式來傳遞異常信息返回到你的主線程。

忽略一個未處理的異常可能是危險的。如果你有一個真正的不可處理的異常(OutOfMemoryException記住),那麼你無法做任何事情,你的過程基本上是註定要失敗的。

返回到.NET 1.1,在backgroundthread未處理的異常也只是拋出無處和主線程會樂意犁上。這可能會帶來討厭的影響。所以在.Net 2.0中,這種行爲已經改變。現在

,未處理的異常在一個線程這不是主線程將終止該進程拋出。您可能會收到此通知(通過訂閱AppDomain上的事件),但該流程將會終止。

由於這可能不方便(當您不知道線程中將運行什麼,並且您不確定是否有適當的防護措施並且您的主線程必須具有彈性)時,有一種解決方法。它的目的是作爲一個傳統的設置(這意味着,強烈建議你確保你沒有雜散的線程),但你可以用這種方式強制以前的行爲:

只需將此設置添加到您的服務/應用程序/任何配置文件:

<configuration> 
    <runtime> 
    <!-- the following setting prevents the host from closing when an unhandled exception is thrown --> 
    <legacyUnhandledExceptionPolicy enabled="1" /> 
    </runtime> 
</configuration> 

它似乎並不與ASP.NET的工作,雖然。

欲瞭解更多信息(和一個巨大的警告,這種設置可能無法在CLR的未來版本將支持)看到http://msdn.microsoft.com/en-us/library/ms228965.aspx

+0

有嘗試...趕上各地的臭味。我只想在未處理的位置只需要一個,並且不希望我的應用程序因爲用戶去點擊「功能X」時發生的簡單的空指針而死亡,並且我們告訴他們使用正在工作的其他功能X按鈕。 。它並沒有關閉應用程序,每次都會讓用戶感到懊惱,就像原來的bug一樣惱火(引導應用程序需要時間,因此會給用戶帶來麻煩) – 2011-12-30 18:31:54

+0

我遇到了有問題的專有外部庫它會使用(內部)`ArgumentNullException`異常地崩潰應用程序。我想爲那個庫啓用`legacyUnhandledExceptionPolicy`。這可以做到嗎? – chris 2015-05-14 11:15:46

7

從喬阿爾巴哈利的優秀threading文章:

.NET Framework提供了全局異常 處理一個 較低級別的事件: AppDomain.UnhandledException。此 事件觸發時,有未處理 例外在任何線程中,並且在任何 類型的應用程序(具有或不具有 用戶界面)。然而,儘管 提供了一個良好的最後手段機制 用於記錄未捕獲的例外,它 提供防止關閉 應用程序沒有辦法 - 和 絕非打壓.NET 未處理的異常對話框。

在生產應用程序中,所有 線程輸入方法都需要明確的 異常處理。人們可以通過使用包裝或輔助 類來執行該作業切割 工作,如 BackgroundWorker的(在 第3部分討論)。

1

這裏是一個偉大的博客文章對這個問題:Handling "Unhandled Exceptions" in .NET 2.0

IMO它會手動處理後臺線程中的異常並在必要時通過回調重新拋出它們是正確的。

delegate void ExceptionCallback(Exception ex); 

void MyExceptionCallback(Exception ex) 
{ 
    throw ex; // Handle/re-throw if necessary 
} 

void BackgroundThreadProc(Object obj) 
{ 
    try 
    { 
    throw new Exception(); 
    } 
    catch (Exception ex) 
    { 
    this.BeginInvoke(new ExceptionCallback(MyExceptionCallback), ex); 
    } 
} 

private void Test() 
{ 
    ThreadPool.QueueUserWorkItem(new WaitCallback(BackgroundThreadProc)); 
} 
4

保持答案的簡短,是的,你可以防止運行時終止。

下面是解決方法的演示:

class Program 
{ 
    void Run() 
    { 
     AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); 

     Console.WriteLine("Press enter to exit."); 

     do 
     { 
      (new Thread(delegate() 
      { 
       throw new ArgumentException("ha-ha"); 
      })).Start(); 

     } while (Console.ReadLine().Trim().ToLowerInvariant() == "x"); 


     Console.WriteLine("last good-bye"); 
    } 

    int r = 0; 

    void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 
    { 
     Interlocked.Increment(ref r); 
     Console.WriteLine("handled. {0}", r); 
     Console.WriteLine("Terminating " + e.IsTerminating.ToString()); 

     Thread.CurrentThread.IsBackground = true; 
     Thread.CurrentThread.Name = "Dead thread";    

     while (true) 
      Thread.Sleep(TimeSpan.FromHours(1)); 
     //Process.GetCurrentProcess().Kill(); 
    } 

    static void Main(string[] args) 
    { 
     Console.WriteLine("..."); 
     (new Program()).Run(); 
    } 
} 

從本質上講,你只是沒有讓運行時顯示「...程序已停止工作」的對話。

如果您需要登錄異常,並默默退出,你可以叫Process.GetCurrentProcess().Kill();

0
AppDomain.CurrentDomain.UnhandledException += (sender, e2) => 
    { 
     Thread.CurrentThread.Join(); 
    }; 

但是要小心,這個代碼將凍結線程和線程的管理對象自身的所有堆棧存儲器。 但是,如果您的應用程序處於確定狀態(可能是您拋出了LimitedDemoFunctionalityException或OperationStopWithUserMessageException),並且您沒有開發全天候應用程序,則此技巧將起作用。

最後,我認爲MS應該允許開發人員從棧頂重寫未處理異常的邏輯。

相關問題