2

単純な BackgroundWorker を実装しようとすると、悪名高い「クロススレッド操作が無効です」という例外が発生します。SOに関する多くの関連する質問を含め、この件に関して見つけることができるすべてを読むのに何時間も費やしましたが、私はそれを理解していません.

DoWork() メソッドを持つ単純な winform があります。

  1. 実行する作業を表すデリゲートが必要です。
  2. を作成し、デリゲートをイベントBackgroundWorkerに割り当てます。DoWork
  3. を呼び出しますRunWorkerAsync()

によって呼び出された関数はRunWorkerCompleted()、フォームのラベルを更新しようとしますが、スレッド間例外をスローします。

Public Class MyForm

    Public Sub DoWork(workToDo As DoWorkEventHandler)
        Dim worker As New BackgroundWorker()
        worker.WorkerReportsProgress = True
        worker.WorkerSupportsCancellation = True
        AddHandler worker.DoWork, workToDo
        AddHandler worker.RunWorkerCompleted, AddressOf WorkerCompleted
        worker.RunWorkerAsync()
    End Sub

    Private Sub WorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
        resultLabel.Text = "Done!" ' Exception thrown here
    End Sub

End Class

WorkerCompletedしたがって、バックグラウンド スレッドで実行されているように見えます。私は実用的な解決策に近づいていますか、それとも基本的なことを理解していませんか?

更新: 謎の修正

ばかげた運で「修正」を発見しました。アプリケーションをさらに調べる必要があります。これは VSTO Excel アドインの一部です。Show()リボンの Load イベントでフォームをインスタンス化し、ボタンがクリックされたときに呼び出していました。これにより例外が発生しました。

Private Sub Ribbon1Load(ByVal sender As System.Object, ByVal e As RibbonUIEventArgs) Handles MyBase.Load
    mProcessing = New MyForm()
End Sub 

Private Sub Button1Click(sender As System.Object, e As RibbonControlEventArgs) Handles Button1.Click
    mProcessing.Show()
    mProcessing.DoWork(AddressOf UpdateData)
End Sub

Click ハンドラーに移動mProcessing = New MyForm()すると、例外がなくなります。すべてがうまくいっています。私はコードを数回前後に動かしましたが、問題/解決策に自信があります。

4

2 に答える 2

2

イベントは通常、それ自体 (UI スレッド)RunWorkerCompletedと同じスレッドで実行されるため、ここで問題があるとは思えません。BackgroundWorkerメソッド中にエラーが発生しDoWork、UI スレッドにヒットするとスローされると思われます。ドキュメントの状態:

RunWorkerCompleted イベント ハンドラーは、RunWorkerCompletedEventArgs.Result プロパティにアクセスする前に、AsyncCompletedEventArgs.Error および AsyncCompletedEventArgs.Cancelled プロパティを常にチェックする必要があります。例外が発生した場合、または操作がキャンセルされた場合、RunWorkerCompletedEventArgs.Result プロパティにアクセスすると例外が発生します。

イベントが UI スレッドで実行されていないという問題が発生した場合は、いつでも UI 更新を呼び出して、たとえば次のように正しいスレッドで実行することができます。RunWorkerCompletedInvoke

resultLabel.Invoke(Sub() resultLabel.Text = "Done!")
于 2013-08-07T12:45:41.913 に答える
1

手に負えないことは何もありませんが、これは起こり得ることです。あまり見たことがありませんが、見たことがあるので、次のように処理する必要がありました。

If Me.InvokeRequired Then
    Me.BeginInvoke(Sub() resultLabel.Text = "Done!")
Else
    resultLabel.Text = "Done!"
End If
于 2013-08-07T12:45:47.727 に答える