交叉线程操作无效…-VB.NET

交叉线程操作无效…-VB.NET,vb.net,visual-studio-2010,multithreading,Vb.net,Visual Studio 2010,Multithreading,我使用的是vb.net,在我的程序中,当我运行backgroundworker时,会出现“crossthread operation not valid”(交叉线程操作无效)错误,这将使此文本框启用为真。我的主sub将首先将enabled设置为false,当backgroundworker运行时,它将返回true,然后退出。为什么它会给我一个错误?仅供参考:这里有更多的代码,但我不想让它变得更加混乱 以下是堆栈跟踪: at System.Windows.Forms.Control.get_Han

我使用的是vb.net,在我的程序中,当我运行backgroundworker时,会出现“crossthread operation not valid”(交叉线程操作无效)错误,这将使此文本框启用为真。我的主sub将首先将enabled设置为false,当backgroundworker运行时,它将返回true,然后退出。为什么它会给我一个错误?仅供参考:这里有更多的代码,但我不想让它变得更加混乱

以下是堆栈跟踪:

at System.Windows.Forms.Control.get_Handle()
   at System.Windows.Forms.Control.OnEnabledChanged(EventArgs e)
   at System.Windows.Forms.Control.set_Enabled(Boolean value)
   at Helium.Form1.BackgroundWorker1_DoWork(Object sender, DoWorkEventArgs e) in C:\Users\Kevin\documents\visual studio 2010\Projects\Helium\Helium\Form1.vb:line 167
   at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
   at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)
下面是确切的错误消息:

{"Cross-thread operation not valid: Control 'mainText' accessed from a thread other than the thread it was created on."}
有人能帮帮我吗

谢谢


KEvin

不能直接从另一个线程设置UI线程上的控件属性。这是可以做到的,这里是一个来自msdn的例子

Private Sub SetText(ByVal [text] As String)

    ' InvokeRequired required compares the thread ID of the'
    ' calling thread to the thread ID of the creating thread.'
    ' If these threads are different, it returns true.'
    If Me.textBox1.InvokeRequired Then
        Dim d As New SetTextCallback(AddressOf SetText)
        Me.Invoke(d, New Object() {[text]})
    Else
        Me.textBox1.Text = [text]
    End If
End Sub

在何处设置Enabled属性?如果在DoWork事件处理程序中执行此操作,则此代码运行在与按钮创建时所在线程不同的线程上,这将给出您遇到的异常。要解决这个问题,您应该使用BeginInvoke。为了方便起见,可以将其包装到一个方法中,如下所示:

Private Sub SetControlEnabled(ByVal ctl As Control, ByVal enabled As Boolean)
    If ctl.InvokeRequired Then
        ctl.BeginInvoke(New Action(Of Control, Boolean)(AddressOf SetControlEnabled), ctl, enabled)
    Else
        ctl.Enabled = enabled
    End If
End Sub
现在,您可以安全地调用该方法来启用或禁用来自任何线程的任何控件:

SetControlEnabled(someButton, False)
BackgroundWorker类的目的是在GUI保持响应的同时在非GUI线程上执行工作。除非您将Control.CheckForIllegalCrossThreadCalls设置为false(您不应该这样做),或者按照其他答案中的建议使用Invoke(我也不建议这样做),否则您将获得非法的跨线程操作异常


如果希望在BackgroundWorker运行时发生与GUI相关的事情,我通常建议使用BackgroundWorker.ReportProgress方法,并将适当的处理程序附加到BackgroundWorker.ProgressChanged事件。如果您想在BackgroundWorker完成后在GUI上发生一些事情,那么只需将处理程序连接到BackgroundWorker.RunWorkerCompleted事件,即可将GUI更新到BackgroundWorker.RunWorkerCompleted事件。

您的表单加载请写在下面的代码部分。你所有的问题都会解决的

'## crossed-thread parts will not be controlled by this option...

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False

在Form1\u Load或您的表单中键入以下代码:

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False

它修复了被阻止的跨线程操作的所有问题。

在VB.NET中解决这个问题的更好方法是使用一个扩展,它为跨线程GUI控件调用生成了非常好看的代码

只需将这行代码添加到您拥有的任何模块中

<System.Runtime.CompilerServices.Extension()> _
Public Sub Invoke(ByVal control As Control, ByVal action As Action)
    If control.InvokeRequired Then
        control.Invoke(New MethodInvoker(Sub() action()), Nothing)
    Else
        action.Invoke()
    End If
End Sub
想在清除后添加一些内容吗

cboServerList.Invoke(Sub() cboServerList.Items.Add("Hello World"))

建议使用AutomationPeer

例如,下面的代码在我按F5键时调用执行按钮。同样,如果希望线程或后台工作程序调用事件或函数,可以使用Automation Peer。在您的例子中,您可以使用文本框及其相应属性来调用,而不是我在这里使用的按钮

'Button name=btnExecute

'Imports System.Windows.Automation.Peers

'Imports System.Windows.Automation.Provider

If e.Key = Key.F5 Then

   Dim peer As New ButtonAutomationPeer(btnExecute)

   Dim invokeProv As IInvokeProvider = TryCast(peer.GetPattern(PatternInterface.Invoke), IInvokeProvider)

   invokeProv.Invoke()

End If
问候
RV

我认为错误消息是不言自明的。那么我应该把textbox1.enabled=true部分放在哪里?+1。使用backgroundworker.reportprogress可以帮助我在线程运行时更新gui。请注意,这个答案是错误的,如中所述。请不要使用它,而是用您的代码修复问题,即创建适当的委托。请添加一些其他说明。
'Button name=btnExecute

'Imports System.Windows.Automation.Peers

'Imports System.Windows.Automation.Provider

If e.Key = Key.F5 Then

   Dim peer As New ButtonAutomationPeer(btnExecute)

   Dim invokeProv As IInvokeProvider = TryCast(peer.GetPattern(PatternInterface.Invoke), IInvokeProvider)

   invokeProv.Invoke()

End If
CheckForIllegalCrossThreadCalls = False