Необработанные исключения в 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.