web-dev-qa-db-ja.com

BackgroundWorkerの未処理の例外

私のWinFormsアプリは、いくつかの BackgroundWorker オブジェクトを使用して、データベースから情報を取得します。 BackgroundWorkerを使用しているのは、データベースクエリが長時間実行されている間もUIのブロックを解除でき、スレッドモデルが簡略化されるためです。

これらのバックグラウンドスレッドの一部で時々DatabaseExceptionsが発生し、デバッグ中にワーカースレッドでこれらの例外の少なくとも1つを目撃しました。これらの例外はタイムアウトであると私はかなり確信しています。

私の質問は、これらのバックグラウンドワーカースレッドの1つで未処理の例外が発生したときに何が起こるかについてです。

別のスレッドで例外をキャッチできるとは思いませんが、WorkerCompletedメソッドが実行されることを期待できますか?例外を問い合わせできるBackgroundWorkerのプロパティまたはメソッドはありますか?

71
Ed Guiness

コードで処理できない例外が操作によって発生した場合、BackgroundWorkerは例外をキャッチしてRunWorkerCompletedイベントハンドラーに渡し、System.ComponentModel.RunWorkerCompletedEventArgsのErrorプロパティとして公開されます。 Visual Studioデバッガーで実行している場合、デバッガーは、未処理の例外が発生したDoWorkイベントハンドラーのポイントで中断します。

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

77
Ed Guiness

私は何年にもわたってBackgroundWorkerを完全に使用しており、本当に深く理解しています。

つい最近、RunWorkerCompletedで単にThrow New Exception("Test")とすると、My DoWorkは_e.Error_をキャッチしません。ただし、未処理の例外が発生しました。 DoWorkでのキャッチはベストプラクティスではないため、_e.Error_は意味を持ちません。

新しいFormで新しいBackgroundWorkerを作成しようとすると、RunWorkerCompletedの_e.Error_が正常に処理されました。複雑なBackgroundWorkerに問題があるはずです。

数日後、グーグルしてデバッグし、エラーを試します。私のRunWorkerCompletedにこれが見つかりました:

  • 最初に_e.Error_をチェックし、次に_e.Cancelled_、最後に_e.Result_を確認します
  • _e.Result_の場合、_e.Cancelled = True_を取得しません。
  • _e.Result_がnull(またはNothing)でない場合、_e.Error_を取得しません**

**これは私が恋しいところです。 _e.Result_がnull(またはNothing)でない場合に_e.Error_を使用しようとすると、未処理の例外がスローされます。


PDATE: _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にアクセスできます。

34
CallMeLaNN

デフォルトでは、BackgroundWorkerによってキャッチおよび保存されます。 MSDNから:

操作でコードが処理しない例外が発生すると、BackgroundWorkerが例外をキャッチし、それをRunWorkerCompletedイベントハンドラーに渡します。そこで、System.ComponentModel.RunWorkerCompletedEventArgsのErrorプロパティとして公開されます。 Visual Studioデバッガーで実行している場合、デバッガーは、未処理の例外が発生したDoWorkイベントハンドラーのポイントで中断します。

10
Stu Mackellar

すでに述べたように:

操作でコードが処理しない例外が発生すると、BackgroundWorkerが例外をキャッチし、それをRunWorkerCompletedイベントハンドラーに渡します。そこで、System.ComponentModel.RunWorkerCompletedEventArgsのErrorプロパティとして公開されます。

これは、元のスレッドを操作しているときはいつでも重要です。たとえば、例外の結果をフォームのある種のラベルに書き込む場合は、BackgroundWorkerのDoWorkで例外をキャッチするのではなく、RunWorkerCompletedEventArgsからe.Errorを処理する必要があります。

リフレクターを使用してBackgroundWorkerコードを分析すると、そのすべてがかなり簡単に処理されていることがわかります。DoWorkはtry-catchブロックで実行され、例外はRunWorkerCompletedに渡されるだけです。これが、DoWorkイベントですべての例外を常にキャッチする「推奨」方法に同意しない理由です。

つまり、元の質問に答えるには:

はい-RunWorkerCompletedが常に起動されることを期待できます。

RunWorkerCompletedのe.Errorを使用して、他のスレッドの例外を確認します。

4
Vedran

これは、デバッガーが接続されていない場合にのみ機能します。VisualStudioから実行すると、デバッガーはDoWorkメソッドでハンドレス例外をキャッチして実行を中断しますが、続行をクリックするとRunWorkerCompletedに到達し、例外を読み取ることができますe.Errorフィールドを介して。

3
TzOk