安全地从线程中提升事件

我在从非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 

您从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()在动态重新创建时准确跟踪打开的表单。

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