2011-09-09 21 views
3

我有一個Task異步運行,並使用任務延續task.ContinueWith(t => ..., CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, taskScheduler)處理異常。是否有可能從任務延續觸發AppDomain的未處理異常處理程序失敗?

這對於處理我知道如何處理的異常(例如WebException)非常適用,但如果拋出類似NullReferenceException的東西,我無法正確處理?

我希望能夠使用AppDomain的未處理的異常處理程序(在我們的例子中記錄錯誤,彈出一個對話框通知用戶,並退出應用程序)處理它,但到目前爲止我還沒有找到一個好這樣做的方法。

簡單地從繼續中重新拋出異常會導致在任務完成時調用未處理的異常處理程序(因爲重新拋出的異常未被發現)。這是不好的,因爲我知道應用程序崩潰後可能是幾秒鐘甚至幾分鐘。

調用Environment.FailFast()會使應用程序崩潰,但不會調用未處理的異常處理程序。

調用Thread.CurrentThread.Abort()調用未處理的異常處理程序,但僅顯示來自ThreadAbortException的信息/堆棧跟蹤。另外,使用這種方法似乎是個不錯的主意。

調用Application.OnThreadException()實際上正是我想要的,除了它需要參考System.Windows.Forms和處理Application.ThreadException,它不適用於例如沒有UI的服務。

調用task.Wait()沒有意義,因爲在我們的情況下,沒有地方可以調用它。如果任務成功,則使用成功繼續來處理結果,如果失敗,則調用異常繼續,並且不能阻塞創建任務的線程(這是任務的整個點)。

我可以直接調用未處理的異常處理程序,但至少有一種情況需要添加我不想在我的應用程序中添加的依賴項。

我錯過了一個明顯的方法來做到這一點?

+1

是的,這使它遠離我的工具箱。你有沒有想過如何調用Dispose()方法呢? –

回答

0

我們用Application.OnThreadException()來解決我們的具體情況。

1

我沒有找到任何BCL類,做類似的工作。所以,我建議你使用一些手寫的靜態類:

static class Crusher 
{ 
    private static readonly AutoResetEvent CrushEvent; 
    private static Exception _exception; 

    static Crusher() 
    { 
     CrushEvent = new AutoResetEvent(false); 
    } 

    public static Thread GetCrushWaitingThread() 
    { 
     return new Thread(WaitForCrush); 
    } 

    static void WaitForCrush() 
    { 
     CrushEvent.WaitOne(); 
     throw _exception; 
    } 

    public static void Crush(Exception exception) 
    { 
     _exception = exception; 
     CrushEvent.Set(); 
    } 
} 

你應該只初始化它在你的應用程序的啓動:

static void Main(string[] args) 
{ 
    AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; 
    Crusher.GetCrushWaitingThread().Start(); 
    ... 

,然後 - 你可以從延續調用它:

task.ContinueWith(Continuation, TaskContinuationOptions.OnlyOnFaulted); 

... 

private static void Continuation(Task obj) 
{ 
    Crusher.Crush(obj.Exception); 
} 

但它不如真正的重新拋出的未處理的異常(它有堆棧跟蹤中的Crusher.WaitForCrush()方法的附加信息)。

0

只需使用您爲AppDomain.UnhandledException實現的相同邏輯,並在繼續中發生異常時顯式調用它。下面是一個例子崩潰處理我用:

public static class CrashHandler 
{ 
    public static void Setup() 
    { 
     AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; 
    } 

    public static Task CrashOnUnhandledException(this Task t) 
    { 
     t.ContinueWith(x => HandleException(t.Exception), TaskContinuationOptions.OnlyOnFaulted); 
     return t; 
    } 

    private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) 
    { 
     HandleException(e.ExceptionObject); 
    } 

    private static void HandleException(object ex) 
    { 
     ShowErrorMessage(ex); 

     Environment.Exit(-1); 
    } 

    private static void ShowErrorMessage(object ex) 
    { 
     // your crash dialog goes here 
    } 
} 

擴展方法CrashOnUnhandledException可以這樣使用爲您節省一些打字,讓你在你的堆棧跟蹤有關呼叫的站點:

task.ContinueWith(t => { /* your logic */ }) 
    .CrashOnUnhandledException(); 
相關問題