安全地从线程中提升事件

时间:2008-11-15 16:17:04

标签: c# vb.net multithreading

我遇到了从非UI线程中引发事件的一些问题,因为我不希望在Form1中添加到线程的每个事件处理程序上处理If me.invokerequired。

我确信我已经在某处阅读了如何使用委托事件(在SO上),但我无法找到它。

Public Class Form1

    Private WithEvents _to As New ThreadedOperation

    Private Sub Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button.Click
        _to.start()
    End Sub

    Private Sub _to_SomthingHappend(ByVal result As Integer) Handles _to.SomthingHappend
        TextBox.Text = result.ToString //cross thread exception here
    End Sub

End Class

Public Class ThreadedOperation

    Public Event SomthingHappend(ByVal result As Integer)
    Private _thread As Threading.Thread

    Public Sub start()
        If _thread Is Nothing Then
            _thread = New Threading.Thread(AddressOf Work)
        End If
        _thread.Start()
    End Sub

    Private Sub Work()
        For i As Integer = 0 To 10
            RaiseEvent SomthingHappend(i)
            Threading.Thread.Sleep(500)
        Next
    End Sub

End Class

2 个答案:

答案 0 :(得分:2)

您从Control派生了您的班级。有点不寻常,但如果控件实际托管在表单上,​​您可以使用Me.Invoke()来编组调用。例如:

  Private Delegate Sub SomethingHappenedDelegate(ByVal result As Integer)

  Private Sub Work()
    For i As Integer = 0 To 10
      Me.Invoke(New SomethingHappenedDelegate(AddressOf SomethingHappenedThreadSafe), i)
      Threading.Thread.Sleep(500)
    Next
  End Sub

  Private Sub SomethingHappenedThreadSafe(ByVal result As Integer)
    RaiseEvent SomthingHappend(result)
  End Sub

如果此类对象实际上未托管在表单上,​​则需要传递对表单的引用,以便它可以调用Invoke():

  Private mHost As Form

  Public Sub New(ByVal host As Form)
    mHost = host
  End Sub

并使用mHost.Invoke()。或者BeginInvoke()。

本书的最后一个技巧是使用主启动表单作为同步对象。这不是完全安全的,但在99%的情况下有效:

  Dim main As Form = Application.OpenForms(0)
  main.Invoke(New SomethingHappenedDelegate(AddressOf SomethingHappenedThreadSafe), i)

请注意,WF中存在一个错误,阻止OpenForms()在动态重新创建时准确跟踪打开的表单。

答案 1 :(得分:1)

如果你想简化所有这些,可以使用一个名为BackgroundWorker的类来处理GUI线程编组。