Vb.net 从另一个线程更新ui中的奇怪事情

Vb.net 从另一个线程更新ui中的奇怪事情,vb.net,multithreading,thread-safety,Vb.net,Multithreading,Thread Safety,我正在开发一个vb.net应用程序,需要做大量的后台工作。我有一个线程不同于我访问url登录网站的主线程,当它完成后,我想更改标签的文本。我在主窗体中添加了这个(我知道这是一个快速代码,但在其他应用程序中我工作得很好) 我也尝试过invoke方法,但结果相同。 线程在另一个类中,它不是主窗体类,但是如果线程在主窗体类中,它就工作,如果它在另一个类中,它就不工作 谢谢 编辑: 我刚才试过: Public Sub UpdateButton(ByVal objs() As Object) If

我正在开发一个vb.net应用程序,需要做大量的后台工作。我有一个线程不同于我访问url登录网站的主线程,当它完成后,我想更改标签的文本。我在主窗体中添加了这个(我知道这是一个快速代码,但在其他应用程序中我工作得很好)

我也尝试过invoke方法,但结果相同。 线程在另一个类中,它不是主窗体类,但是如果线程在主窗体类中,它就工作,如果它在另一个类中,它就不工作

谢谢

编辑: 我刚才试过:

Public Sub UpdateButton(ByVal objs() As Object)
    If Me.InvokeRequired Then
        Me.Invoke(New Action(Of Object())(AddressOf UpdateButton), objs)
        Return
    End If
    CType(objs(0), Button).Text = objs(1).ToString
End Sub
编辑2:

新的变化不会成功

  Public Sub UpdateButton(ByVal objs As List(Of Object))
    If CType(objs(0), Button).InvokeRequired Then
        CType(objs(0), Button).Invoke(New Action(Of List(Of Object))(AddressOf UpdateButton), objs)
        Return
    End If
    CType(objs(0), Button).Text = objs(1).ToString
End Sub
我更改了它,因为它给了我一个元素数错误

编辑3:我发现我解释得不好

我得到了一个名为Form1的表单和一个名为Functions的模块。在模块函数中,我定义了我的应用程序拥有的四个线程及其函数,每个线程有两个函数start[thread name]和stop[thread name]我在其中启动和停止线程。然后我在表单1中放置一个按钮,当用户单击它时,它调用我创建的线程的启动功能。例如,我的应用程序有一个名为session_starter的线程,因此当用户单击登录按钮时,我从该按钮调用startSession,当它完成时,线程函数的最后一行从form1调用updatebutton,当我调用它时,按钮的文本将更改为0

我已经把updatebutton做得最普遍了,因为我有很多按钮(大约30个!!),而且每次更改按钮都会有一个函数,这有点慢而且烦人

编辑4: 我做了一些实验。我在表单1中放置了一个按钮,并在按钮上添加了以下代码:

Private Sub Button10_Click_1(sender As Object, e As EventArgs) Handles Button10.Click
    Button9.Text = "hi"
    Dim Evaluator = New Thread(Sub() Me.UpdateButton(Button9, "test"))
    MsgBox("Text after setting from another thread: " & Button9.Text)
    UpdateButton(Button9, "test")
    MsgBox("Text after setting from ui thread: " & Button9.Text)
End Sub
这就是我得到的: 从另一个线程设置后的文本:hi 从ui线程设置后的文本:测试

我不知道为什么会这样。我使用的代码是:

 Public Sub UpdateButton(ByVal btn As Button, ByVal str As String)
    If btn.InvokeRequired Then
        btn.Invoke(New Action(Of Button, String)(AddressOf UpdateButton), btn, str)
        Return
    End If
    btn.Text = str
End Sub

您不能从创建UI时所在的线程以外的线程直接更新UI

访问Windows窗体控件本身不是线程安全的。如果有两个或多个线程操纵控件的状态,则可能会强制控件进入不一致的状态。其他与线程相关的错误也可能存在,包括争用条件和死锁。确保以线程安全的方式访问控件非常重要

NET Framework可帮助您检测何时以非线程安全的方式访问控件。在调试器中运行应用程序时,如果创建控件的线程以外的线程尝试调用该控件,调试器将引发InvalidOperationException,并显示消息“从创建控件的线程以外的线程访问控件控件名”

下面的代码来自上面的链接

' The following example demonstrates the 'Invoke(Delegate)' method of 'Control class. 
' A 'ListBox' and a 'Button' control are added to a form, containing a delegate 
' which encapsulates a method that adds items to the listbox.This function is executed 
' on the thread that owns the underlying handle of the form. When user clicks on button 
' the above delegate is executed using 'Invoke' method. 

Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Threading

Public Class MyFormControl
   Inherits Form

   Delegate Sub AddListItem()
   Public myDelegate As AddListItem
   Private myButton As Button
   Private myThread As Thread
   Private myListBox As ListBox

   Public Sub New()
      myButton = New Button()
      myListBox = New ListBox()
      myButton.Location = New Point(72, 160)
      myButton.Size = New Size(152, 32)
      myButton.TabIndex = 1
      myButton.Text = "Add items in list box" 
      AddHandler myButton.Click, AddressOf Button_Click
      myListBox.Location = New Point(48, 32)
      myListBox.Name = "myListBox"
      myListBox.Size = New Size(200, 95)
      myListBox.TabIndex = 2
      ClientSize = New Size(292, 273)
      Controls.AddRange(New Control() {myListBox, myButton})
      Text = " 'Control_Invoke' example"
      myDelegate = New AddListItem(AddressOf AddListItemMethod)
   End Sub 'New 

   Shared Sub Main()
      Dim myForm As New MyFormControl()
      myForm.ShowDialog()
   End Sub 'Main

   Public Sub AddListItemMethod()
      Dim myItem As String 
      Dim i As Integer 
      For i = 1 To 5
         myItem = "MyListItem" + i.ToString()
         myListBox.Items.Add(myItem)
         myListBox.Update()
         Thread.Sleep(300)
      Next i
   End Sub 'AddListItemMethod

   Private Sub Button_Click(sender As Object, e As EventArgs)
      myThread = New Thread(New ThreadStart(AddressOf ThreadFunction))
      myThread.Start()
   End Sub 'Button_Click

   Private Sub ThreadFunction()
      Dim myThreadClassObject As New MyThreadClass(Me)
      myThreadClassObject.Run()
   End Sub 'ThreadFunction
End Class 'MyFormControl


' The following code assumes a 'ListBox' and a 'Button' control are added to a form,  
' containing a delegate which encapsulates a method that adds items to the listbox. 
Public Class MyThreadClass
   Private myFormControl1 As MyFormControl

   Public Sub New(myForm As MyFormControl)
      myFormControl1 = myForm
   End Sub 'New 

   Public Sub Run()
      ' Execute the specified delegate on the thread that owns 
      ' 'myFormControl1' control's underlying window handle.
      myFormControl1.Invoke(myFormControl1.myDelegate)
   End Sub 'Run

End Class 'MyThreadClass

简单的跨线程更新:

Public Sub UpdateButton(btn As Button, text As String)
  btn.Text = text
End Sub
用法:

'from other thread
 Me.Invoke(Sub() 
             UpdateButton(Button9, "some text")
           End Sub)

上面的代码并不使跨线程调用合法。显示您正在尝试的代码。在线程结束之前,我执行以下操作:Form1.Button3.Text=“Logged In”然后线程结束该代码对我来说毫无意义。请解释你想做什么。你不需要线程来更改按钮上的文本???谢谢。但我说我已经尝试过了,但没有成功。请发布你尝试过的代码,以便我可以根据你的情况定制答案。我几天前从代码中删除了它,现在我正在使用我上面所说的:在form1\u load Control.CheckForIllegalCrossThreadCalls=False中,然后在线程form1.Button3.Text=“Logged-In”中,正如其他人所说,这并不能防止跨线程调用非法。它只会阻止抛出错误,而不会发生错误。请参阅第一条消息“我已使用新代码对其进行了更新,我尝试了该代码,并向我提供了无效的操作异常。我只添加了CType(objs(0),Button)。Text=objs(1)。要字符串,您可以演示如何调用该方法吗?您是如何处理线程的?Form1.UpdateButtonTest({Form1.Button9,“Test”})为什么要使用此调用约定?您是否从差异表单调用form1?向我们展示调用调用此
UpdateButton
方法的新线程的代码。
'from other thread
 Me.Invoke(Sub() 
             UpdateButton(Button9, "some text")
           End Sub)