Бросать исключения в методе обратного вызова для таймеров - PullRequest
32 голосов
/ 12 ноября 2009

Мне нигде не удалось найти ответ на этот вопрос ...

Что происходит с исключениями, сгенерированными в методе обратного вызова для System.Threading.Timer, (или в обработчике событий для System.Timers.Timer). Передано ли исключение потоку, в котором был создан таймер, или оно потеряно?

Каковы побочные эффекты создания исключения в функциях обратного вызова таймера?

Как правильно будет сигнализировать потоку создания таймера о том, что было сгенерировано исключение в рабочем потоке (метод обратного вызова)?

Спасибо за ваше время.

Ответы [ 4 ]

35 голосов
/ 12 ноября 2009

Исключение не передается обратно вызывающему потоку. Если вы хотите, чтобы это было, вы можете добавить блок catch и найти способ сообщить вызывающему потоку. Если вызывающий поток является потоком пользовательского интерфейса WinForms или WPF, вы можете использовать класс SynchronizationContext для передачи вызова в поток пользовательского интерфейса. В противном случае вы можете использовать потокобезопасную очередь (или синхронизирующую блокировку) и периодически проверять ее в другом потоке.

System.Timers.Timer будет молча проглатывать исключения и продолжать таймер (хотя это может измениться в будущих версиях фреймворка); System.Threading.Timer завершит работу программы.

1 голос
/ 12 ноября 2009

Я не знаю, какой вариант лучше, но когда я использую таймер обратного вызова, я обычно выбрасываю исключения и позволяю им всплывать в основной процедуре обратного вызова, где я изящно обрабатываю их. Поток продолжает работать по таймеру, как и должно.

Необработанные исключения в потоке (System.Threading.Timer) остановят всю вашу программу.

0 голосов
/ 28 июня 2018

Из моего скромного теста под Windows 10 framework 4.6 поток, используемый событием SystemTimers.Timer, не распространит необработанное исключение. Мне нужно было запустить событие в главном потоке, чтобы уведомить, что произошло необработанное исключение.

0 голосов
/ 27 сентября 2012

Я не знаю, является ли это лучшим решением, но я сделал небольшой обходной путь. Мой вызывающий поток подписался на событие для отлова исключений из всех потоков. Поэтому, когда исключение возникает в каком-то потоке, скажем, в событии TimerElapsed, тогда из блока catch я поднимаю объект исключения, передающий событие, в качестве аргумента события.

EventHolderCallingClass: он должен определять делегат и событие, как показано ниже.

public class EventHolderCallingClass
{    
  public delegate void HandleExceptionEventDelegate(Exception exception);
  public event HandleExceptionEventDelegate HandleExceptionEvent ;

  void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
  {
    try
    {
      //some operation which caused exception.
    }
    catch(Exception exception)
    {
      if(HandleExceptionEvent!=null)
        HandleExceptionEvent(exception) 
    }
  }
}

Класс обработчика событий (обработчик исключений):

    public EventHandlerClassConstructor()
    {
            EventHolderCallingClass.HandleExceptionEvent += new EventHolderCallingClass.HandleExceptionEventDelegate(HandleExceptionEventHandler);            
    }

    void HandleExceptionEventHandler(Exception exception)
    {
        //handle exception here.
    }
...