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

Другие вопросы по тегам