2

在尝试实现一个简单的 BackgroundWorker 时,我遇到了臭名昭著的“跨线程操作无效”异常。我花了几个小时阅读我能找到的关于这个主题的所有内容,包括关于 SO 的许多相关问题,但我就是不明白。

我有一个简单的 Winform,它有一个 DoWork() 方法:

  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 加载项的一部分。我在功能区 Load 事件中实例化我的表单,然后Show()在单击按钮时调用。这引发了异常。

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

移至mProcessing = New MyForm()Click 处理程序可消除异常。一切运作良好。我多次来回移动代码,并对问题/解决方案充满信心。

4

2 回答 2

2

RunWorkerCompleted事件通常与BackgroundWorker它本身(UI 线程)在同一个线程上运行,所以我怀疑这就是问题所在。我怀疑您的错误是在该DoWork方法期间发生的,并且一旦您点击 UI 线程就会被抛出。文档状态:

在访问 RunWorkerCompletedEventArgs.Result 属性之前,您的 RunWorkerCompleted 事件处理程序应始终检查 AsyncCompletedEventArgs.Error 和 AsyncCompletedEventArgs.Cancelled 属性。如果引发异常或取消操作,则访问 RunWorkerCompletedEventArgs.Result 属性会引发异常。

如果您遇到RunWorkerCompleted事件未在 UI 线程上运行的问题,那么您始终可以调用 UI 更新以在正确的线程上运行,Invoke例如

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 回答