crossthreading cross class vb.net问题

时间:2014-06-27 02:04:20

标签: vb.net multithreading function

我有我的表单类,第二个模块有一些特殊功能, 当我单击我的表单上的一个按钮时,我在一个单独的线程中运行第二个模块的公共函数(后来运行第二个模块中的其他公共函数),我设置了SetApartmentState(ApartmentState.STA),我尝试使用deletage sub和CheckForIllegalCrossThreadCalls = False,但问题保持不变,我的线程函数(在第二个模块上)无法访问我的表单控件,但是当我将函数移动到表单类时,一切都再次工作,你建议解决什么这个问题?

Public Class Form1
Dim T0 As Thread

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    CheckForIllegalCrossThreadCalls = False
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    T0 = New Thread(AddressOf sub1)
    T0.SetApartmentState(ApartmentState.STA)
    T0.start()
End Sub
End Class


Module Module1

Public Sub Sub1()
msgbox(form1.textbox1.text)    'even if the textbox contains content it returns ""    
Function2()
End Sub

Public Function Function1()
'SomeInstructions
msgbox(form1.textbox1.text)    'same problem here
End Function

End module
PS:它在调试时没有给出任何错误或停止代码,我试图将sub1放在表单类和模块中的其他函数中,但是,只有他sub1才能访问控件,我试过代表,但我不知道我做得对,请有人提出任何建议

1 个答案:

答案 0 :(得分:2)

这里有两个问题。

其中之一是由于默认表单在VB.NET中的工作方式。请参阅,在C#中,您需要一个类型为Form1的具体实例才能访问其非静态成员,而在VB.NET中Form1 看起来像一样的表单实例允许你写Form1.TextBox1.Text之类的东西。当您从UI线程访问Form1的成员时,这可以正常工作,但是当您尝试从后台线程获取它时,会创建Form1 new 实例,并且该线程看到的Form1.TextBox1实际上指向TextBox的完全不同的实例。

另一种说法是VB.NET中的默认表单实例是线程静态

解决这个问题的方法是保留对Form1的具体引用,以便您可以传递它。

当你去

Dim myForm As New Form1

......或

Dim myForm As Form1 = Me

...指向特定Form1实例的'myForm'变量可以像任何其他引用一样在线程之间传递,并且不会改变其含义。

但是,这给我们带来了问题#2:

您不应该访问UI控件(这意味着来自 Control 的任何类型,包括 Form ,来自创建它的线程以外的线程。

如果您必须这样做,则必须将访问Control的调用封送到创建它的线程,例如使用Invoke / BeginInvoke(还有其他方式也是如此。

这是对代码的修改以实现您想要的,以及演示多线程“切换”的更复杂的示例:在UI线程上收集有趣的状态,在后台线程上执行它的工作,然后在UI线程。

Imports System.Threading

Public Class Form1

  Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    ' We don't need this anymore.
    ' We'll do things right and access
    ' the UI on the UI thread only.
    ' CheckForIllegalCrossThreadCalls = False
  End Sub

  Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    ' Note that we're passing a reference
    ' to THIS instance of Form1 to Sub1 and Sub2.
    Dim form As Form1 = Me

    ' Let's spin up some threads.
    Dim T0 As New Thread(Sub() Module1.Sub1(form))

    T0.Start()

    Dim T1 As New Thread(Sub() Module1.Sub2(form))

    T1.Start()
  End Sub

End Class

Module Module1

  ' Note that this sub now accepts
  ' a reference to an instance of Form1.
  Public Sub Sub1(form As Form1)
    ' This is what we want to do:
    Dim action As New Action(Sub() MsgBox(form.TextBox1.Text))

    ' See if we're on the right thread.
    If form.InvokeRequired Then
      ' Invoke on the thread which created this Form1 instance.
      form.Invoke(action)
    Else
      ' Invoke on the current thread.
      action.Invoke()
    End If
  End Sub

  ' This is a more complex example.
  Public Sub Sub2(form As Form1)
    ' This function will get the text from TextBox1 when invoked.
    ' It still needs to be invoked on the UI thread though.
    Dim getText As New Func(Of String)(Function() form.TextBox1.Text)

    Dim text As String

    If form.InvokeRequired Then
      text = CStr(form.Invoke(getText))
    Else
      text = getText() ' Shorthand syntax.
    End If

    ' Now that we have the text, let's do some
    ' intensive work with it while we're on
    ' the background thread.
    For i = 0 To 5
      text &= i

      Thread.Sleep(100)
    Next

    ' Now we want to show the message box - again, on the UI thread.
    Dim showMessageBox As New Action(Sub() MsgBox(text))

    If form.InvokeRequired Then
      form.Invoke(showMessageBox)
    Else
      showMessageBox()
    End If
  End Sub

End Module