简体   繁体   中英

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

I need for exceptions thrown within a timed event to be bubbled up and handled outside of the event handler context. I had read that System.Threading.Timers would be able to do that, provided that, as outlined in the answer to this question , the exception is caught in the callback method and a mechanism is used to re-throw it. Using this example as a guide, I created an event handler which throws and a method which should catch and re-throw the exception using an 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);
    }
}

I then wrote a unit test to see if the exception would bubble up:

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

The test case fails indicating no exception was thrown. If I debug the test case, I can clearly see that the exception is caught and re-thrown in ThrowerThreaded() however the method still continues and exists normally. Why is the exception still being suppressed?

I'm guessing ThrowerThreaded is running on a background thread. That means it does not have a synchronizationContext , since these are intended to synchronize UI applications. This means the callback is called on the threadpool:

Any handler provided to the constructor or event handlers registered with the ProgressChanged event are invoked through a SynchronizationContext instance captured when the instance is constructed. If there is no current SynchronizationContext at the time of construction, the callbacks will be invoked on the ThreadPool .

Rethrowing the exception on a threadpool thread will probably kill that thread, and I'm somewhat surprised it did not kill the application, but it's possible that such behavior is overridden by the testing framework you are using.

To solve this you really need to handle the exception in the callback instead of re-throwing it. If you are not handling the exception, who should? there is a unhandledExceptionEvent , but that is intended for logging before you close your app.

You could handle the exception taking a callback in ThrowerThreaded that you delegate the exception handling to. Another alternative would be to create a TaskCompletionSource that allow you to return a task, and set the task to 'failed' by calling SetException on the source.

It is also poor practice to re-throw the same exception object, since you will lose the call stack, you should instead wrap the exception in a new exception that is thrown.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM