Необработанные исключения в BackgroundWorker - PullRequest
71 голосов
/ 03 ноября 2008

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

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

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

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

Ответы [ 5 ]

77 голосов
/ 03 ноября 2008

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

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

34 голосов
/ 14 апреля 2010

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

Совсем недавно My 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), будет выдано необработанное исключение.


UPDATE: В свойстве e.Result get .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 где угодно.

10 голосов
/ 03 ноября 2008

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

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

4 голосов
/ 20 июня 2011

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

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

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

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

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

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

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

3 голосов
/ 26 августа 2011

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

...