繁体   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