Необработанные исключения в BackgroundWorker
Мое приложение WinForms использует несколько объектов BackgroundWorker для извлечения информации из базы данных. Я использую BackgroundWorker, потому что он позволяет интерфейсу оставаться разблокированным во время длительных запросов к базе данных и упрощает для меня модель потоков.
Я получаю случайные исключения DatabaseException в некоторых из этих фоновых потоков, и во время отладки я видел по крайней мере одно из этих исключений в рабочем потоке. Я вполне уверен, что эти исключения являются тайм-аутами, которые, я полагаю, время от времени разумно ожидать.
Мой вопрос о том, что происходит, когда в одном из этих фоновых рабочих потоков возникает необработанное исключение.
Я не думаю, что смогу перехватить исключение в другом потоке, но могу ли я ожидать выполнения моего метода WorkerCompleted? Есть ли какое-либо свойство или метод BackgroundWorker, который я могу запросить для исключений?
5 ответов
Если операция вызывает исключение, которое ваш код не обрабатывает, BackgroundWorker
ловит исключение и передает его в RunWorkerCompleted
обработчик события, где он отображается как свойство Error System.ComponentModel.RunWorkerCompletedEventArgs
, Если вы работаете в отладчике Visual Studio, он прервется в той точке обработчика событий DoWork, где возникло необработанное исключение.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.dowork.aspx
Я полностью использую BackgroundWorker
в течение многих лет и действительно знаю это глубоко.
Совсем недавно мой RunWorkerCompleted
не ловит e.Error
когда я просто Throw New Exception("Test")
в DoWork
, Однако необработанное исключение поднято. Ловить в DoWork
таким образом, это не лучшая практика e.Error
не имеет смысла
Когда я пытаюсь создать новый Form
с новым BackgroundWorker
, e.Error
в RunWorkerCompleted
обработано успешно. Там должно быть что-то не так в моем сложном BackgroundWorker
,
После нескольких дней погуглил и отладил, попробовал ошибку. Я нашел это в моем RunWorkerCompleted
:
- Проверить
e.Error
будет первыйe.Cancelled
И наконецe.Result
- Не получить
e.Result
еслиe.Cancelled = True
, - Не получить
e.Result
еслиe.Error
не являетсяnull
(или жеNothing
) **
** Это где я скучаю. Если вы пытаетесь использовать e.Result
если e.Error
не является null
(или же Nothing
), Необработанное исключение будет брошено.
ОБНОВЛЕНИЕ: В e.Result
получить свойство.NET дизайн, чтобы проверить e.Error
во-первых, если получена ошибка, то они будут перебрасывать то же исключение из DoWork
, Вот почему мы получаем необработанное исключение в 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
где угодно.
По умолчанию он будет перехвачен и сохранен в BackgroundWorker. Из MSDN:
Если операция вызывает исключение, которое ваш код не обрабатывает, BackgroundWorker перехватывает исключение и передает его в обработчик событий RunWorkerCompleted, где оно отображается как свойство Error System.ComponentModel.RunWorkerCompletedEventArgs. Если вы работаете в отладчике Visual Studio, он прервется в той точке обработчика событий DoWork, где возникло необработанное исключение.
Как уже было отмечено:
Если операция вызывает исключение, которое ваш код не обрабатывает, BackgroundWorker перехватывает исключение и передает его в обработчик событий RunWorkerCompleted, где оно отображается как свойство Error System.ComponentModel.RunWorkerCompletedEventArgs.
Это важно всякий раз, когда вы взаимодействуете с оригинальной веткой. Например, если вы хотите, чтобы результат вашего исключения был записан в виде метки в форме, тогда вам не нужно перехватывать исключение в DoWork в BackgroundWorker, а вместо этого обрабатывать ошибку e.Error из RunWorkerCompletedEventArgs.
Если вы проанализируете код BackgroundWorker с помощью рефлектора, вы увидите, что все это обрабатывается довольно просто: ваш DoWork выполняется в блоке try-catch, а исключение просто передается в RunWorkerCompleted. По этой причине я не согласен с "предпочтительным" методом, позволяющим всегда перехватывать все ваши исключения в событии DoWork.
Короче говоря, чтобы ответить на оригинальный вопрос:
Да - вы можете рассчитывать на то, что ваш RunWorkerCompleted всегда будет уволен.
Используйте e.Error из RunWorkerCompleted для проверки исключений в другом потоке.
Это будет работать только без подключенного отладчика. При запуске из Visual Studio отладчик будет перехватывать необработанное исключение в методе DoWork и прерывать выполнение, однако вы можете нажать продолжить, и будет достигнут RunWorkerCompleted, и вы сможете прочитать исключение. через поле e.Error.