簡體   English   中英

在 System.Threading.Timers TimerCallback 事件處理程序中捕獲異常,然后重新拋出未發送回主線程

[英]Exception caught in System.Threading.Timers TimerCallback event handler and then re-thrown not sent back to main thread

我需要在定時事件中拋出的異常在事件處理程序上下文之外冒泡和處理。 我讀過System.Threading.Timers可以做到這一點,前提是,正如這個問題的答案中所概述的那樣,在回調方法中捕獲異常並使用一種機制重新拋出它。 使用此示例作為指南,我創建了一個拋出的事件處理程序和一個應該使用IProgress object 捕獲並重新拋出異常的方法:

void ThrowerThreaded() {
    var progress = new Progress<Exception>((ex) => {
        throw ex;
    });
    Timer timer = new Timer(x => onTimerElapsedThrow2(progress), null, 10, -1);
    Thread.Sleep(1000);
}

void onTimerElapsedThrow2(IProgress<Exception> progress) {
    try {
        throw new Exception();
    } catch (Exception ex) {
        progress.Report(ex);
    }
}

然后我寫了一個單元測試來查看異常是否會冒泡:

[TestMethod]
public void TestThreadedTimerThrows() {
    Assert.ThrowsException<Exception>(ThrowerThreaded);
}

測試用例失敗表明沒有拋出異常。 如果我調試測試用例,我可以清楚地看到異常被捕獲並在ThrowerThreaded()中重新拋出,但是該方法仍然繼續並正常存在。 為什么異常仍然被抑制?

我猜ThrowerThreaded正在后台線程上運行。 這意味着它沒有synchronizationContext ,因為它們旨在同步 UI 應用程序。 這意味着在線程池上調用了回調:

提供給構造函數的任何處理程序或注冊到 ProgressChanged 事件的事件處理程序都通過構造實例時捕獲的 SynchronizationContext 實例調用。 如果在構建時沒有當前 SynchronizationContext,回調將在 ThreadPool 上調用

在線程池線程上重新拋出異常可能會終止該線程,我有點驚訝它沒有終止應用程序,但您正在使用的測試框架可能會覆蓋這種行為。

要解決這個問題,您確實需要在回調中處理異常而不是重新拋出它。 如果您不處理異常,誰來處理? 有一個unhandledExceptionEvent ,但它用於在您關閉應用程序之前記錄日志。

您可以在將異常處理委托給的ThrowerThreaded中進行回調來處理異常。 另一種選擇是創建一個允許您返回任務的TaskCompletionSource ,並通過在源上調用 SetException 將任務設置為“失敗”。

重新拋出相同的異常 object 也是不好的做法,因為您將丟失調用堆棧,您應該將異常包裝在拋出的新異常中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM