交叉操作无效... - VB.NET

时间:2010-02-10 22:07:11

标签: vb.net visual-studio-2010 multithreading

我正在使用vb.net,并且在我的程序中,当我运行我的后台工作程序时,我会得到这个'crossthread操作无效'错误,这将使此文本框启用为true。我的主要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."}

有人可以帮帮我!

谢谢,

凯文

8 个答案:

答案 0 :(得分:21)

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

如果您想要与GUI相关的“内容”发生您的BackgroundWorker正在运行,我通常建议使用BackgroundWorker.ReportProgress方法并附加适当的处理程序BackgroundWorker.ProgressChanged事件。如果您希望在BackgroundWorker 完成后在GUI上发生某些事情,则只需附加处理程序即可将GUI更新为BackgroundWorker.RunWorkerCompleted事件。

答案 1 :(得分:12)

Form1_Load(或任何形式)sub:

中键入以下代码
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False

它解决了阻塞的跨线程操作的所有问题。

答案 2 :(得分:10)

您在何处设置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)

答案 3 :(得分:8)

在VB.NET中更好的方法是使用Extension它为跨线程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

现在,您可以编写跨线程控制代码,该代码对于任何控制调用只有1行长。

就像这样,假设您要清除一个ComboBox并且它是从线程调用的,或者没有线程,您可以立即使用它来执行此操作

cboServerList.Invoke(Sub() cboServerList.Items.Clear())

想要在清除后添加内容吗?

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

答案 4 :(得分:2)

您不能直接从另一个线程设置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

答案 5 :(得分:1)

你的Form_Load()请写下面的代码部分。你的所有问题都将得到解决。

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

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False

答案 6 :(得分:0)

建议使用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

答案 7 :(得分:-1)

CheckForIllegalCrossThreadCalls = False