Необработанные исключения в BackgroundWorker

В моем приложении WinForms используется несколько объектов BackgroundWorker для извлечения информации из базы данных. Я использую BackgroundWorker, потому что он позволяет UI оставаться разблокированным во время длительных запросов к базе данных, и это упрощает модель потоков для меня.

Я получаю случайные DatabaseExceptions в некоторых из этих фоновых потоков, и я был свидетелем хотя бы одного из этих исключений в рабочем потоке во время отладки. Я довольно уверен, что эти исключения - это таймауты, которые, я полагаю, разумно ожидать от них время от времени.

Мой вопрос о том, что происходит, когда необработанное исключение возникает в одном из этих потоков рабочего потока.

Я не думаю, что могу поймать исключение в другом потоке, но могу ли я ожидать, что мой метод WorkerCompleted будет выполнен? Есть ли какое-либо свойство или метод BackgroundWorker, который я могу опросить для исключений?

Ответ 1

Если операция вызывает исключение, которое ваш код не обрабатывает, BackgroundWorker выполняет исключение и передает его в обработчик события RunWorkerCompleted, где он отображается как свойство Error System.ComponentModel.RunWorkerCompletedEventArgs. Если вы работаете под отладчиком Visual Studio, отладчик будет разбиваться в точке обработчика событий DoWork, где было обработано необработанное исключение.

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.dowork.aspx

Ответ 2

Я полностью использую BackgroundWorker в течение многих лет и действительно знаю это глубоко.

Совсем недавно My RunWorkerCompleted не улавливает e.Error, когда я просто Throw New Exception("Test") в DoWork. Однако необработанное исключение поднято. Поймать в DoWork не самая лучшая практика, поэтому e.Error не получил никакого значения.

Когда я пытаюсь создать новый Form с новым BackgroundWorker, e.Error in RunWorkerCompleted успешно обработанным. В моем сложном BackgroundWorker должно быть что-то не так.

Через несколько дней googling и отладка, попробовав ошибку. Я нашел это в своем RunWorkerCompleted:

  • Сначала проверьте e.Error, затем e.Cancelled и, наконец, e.Result
  • Не получайте e.Result, если e.Cancelled = True.
  • Не получайте e.Result, если e.Error не null (или Nothing) **

** Здесь я скучаю. Если вы пытаетесь использовать e.Result, если e.Error не null (или Nothing), будет отменено Unhandled Exception.


UPDATE: В e.Result получить свойство .NET, чтобы сначала проверить e.Error, если получена ошибка, тогда они будут повторно выбрасывать одно и то же исключение из DoWork. Вот почему мы получаем исключение Unhandled в RunWorkerCompleted, но на самом деле исключение исходит от DoWork.

Вот наилучшая практика в RunWorkerCompleted:

If e.Error IsNot Nothing Then
  ' Handle the error here
Else
  If e.Cancelled Then
    ' Tell user the process canceled here
  Else
    ' Tell user the process completed
    ' and you can use e.Result only here.
  End If
End If

Если вы хотите, чтобы объект, доступный для всех DoWork, ProgressChanged и RunWorkerCompleted, использовал следующее:

Dim ThreadInfos as Dictionary(Of BackgroundWorker, YourObjectOrStruct)

Вы можете легко получить доступ к ThreadInfos(sender).Field в любом месте.

Ответ 3

По умолчанию он будет пойман и сохранен в BackgroundWorker. Из MSDN:

Если операция вызывает исключение, которое ваш код не обрабатывает, BackgroundWorker ловит исключение и передает его в обработчик события RunWorkerCompleted, где он отображается как свойство Error для System.ComponentModel.RunWorkerCompletedEventArgs. Если вы работаете под отладчиком Visual Studio, отладчик будет разбиваться в точке обработчика событий DoWork, где было обработано необработанное исключение.

Ответ 4

Как уже отмечалось:

Если операция вызывает исключение что ваш код не обрабатывает, BackgroundWorker ловит исключение и передает его в Обработчик события RunWorkerCompleted, где он отображается как Ошибка свойство System.ComponentModel.RunWorkerCompletedEventArgs.

Это важно, когда вы взаимодействуете с исходным потоком. Например, если вы хотите, чтобы результат вашего исключения был написан каким-то ярлыком в вашей форме, когда вы не должны улавливать исключение в DoWork BackgroundWorker, а вместо этого обрабатываете e.Error из RunWorkerCompletedEventArgs.

Если вы проанализируете код BackgroundWorker с помощью отражателя, вы можете увидеть, что все его обработано довольно просто: Ваша DoWork выполняется в блоке try-catch, и исключение просто передается RunWorkerCompleted. Именно по этой причине я не согласен с "предпочтительным" методом всегда улавливать все ваши исключения в событии DoWork.

Короче говоря, чтобы ответить на исходный вопрос:

Да - вы можете рассчитывать, что ваш RunWorkerCompleted всегда будет запущен.

Используйте e.Error из RunWorkerCompleted для проверки исключений в другом потоке.

Ответ 5

Это будет работать только без приложенного отладчика при запуске из Visual Studio, отладчик поймает неиспользуемое исключение в методе DoWork и нарушит выполнение, однако вы можете нажать "продолжить", а RunWorkerCompleted будет достигнут, и вы сможете для чтения исключений через поле e.Error.