跨线程操作无效... - VB.NET

问题描述 投票:0回答:9

我正在使用 vb.net,在我的程序中,当我运行后台工作程序时,我会收到此“跨线程操作无效”错误,这将使此文本框启用为 true。我的主子程序将首先将启用设置为 false,当后台工作程序运行时,它会将其设置为 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."}

有人可以帮帮我吗!

谢谢,

凯文

vb.net visual-studio-2010 multithreading
9个回答
26
投票

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

如果您希望在 BackgroundWorker 运行时发生与 GUI 相关的“事情”,我通常建议使用

BackgroundWorker.ReportProgress
方法并将适当的处理程序附加到
BackgroundWorker.ProgressChanged
事件。如果您希望在
BackgroundWorker
finished
后 GUI 上发生某些事情,则只需附加处理程序即可将 GUI 更新到 BackgroundWorker.RunWorkerCompleted 事件。
    


14
投票
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)



14
投票
Form1_Load

(或任何您的表单)子中键入以下代码:


System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False

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


14
投票
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 行长的跨线程控制代码。

像这样,假设您想清除一个组合框,并且它是从线程调用的,或者没有线程,您现在就可以使用它

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

清除后还想添加一些东西吗?

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



2
投票

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



0
投票

'## crossed-thread parts will not be controlled by this option... System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False



0
投票

例如,当我按 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

问候
房车


0
投票


0
投票
BackgroundWorker

中编写 UI 交互代码并且不会遇到跨线程操作的方法。有一天我把这个放在一起做一个项目,买家要小心!

' Init background worker
Dim _BGWorker As BackgroundWorker

' Your Windows Form's _Load event handler:
Private Sub Form_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    ' Just for this example, we're newing up the _BGWorker here
    _BGWorker = New BackgroundWorker()
    _BGWorker.WorkerReportsProgress = True
End Sub

' Magical UI Action Handling ProgressChanged Event Handler Thing (v24.4.550-alpha9) ™ ©
Private Sub _BGWorker_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles _BGWorker.ProgressChanged
    ' Take the UserState object and cast to an Action delegate type
    Dim uiAction As Action = CType(e.UserState, Action)

    ' Check if an action was passed
    If uiAction IsNot Nothing Then
        ' Run it if so!
        uiAction()
    End If
End Sub

' Standard DoWork handler for BackroundWorker
Private Sub _BGWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles _BGWorker.DoWork
    '...your background worker code...

    ' EXAMPLE:
    '
    ' Me.Text = "Background worker is (trying to) change form title! :-O"
    '
    ' This line would normally fail with an exception when written directly in the DoWork
    ' handler of a BackgroundWorker. Exception you would see is:
    '   System.InvalidOperationException: Cross-thread operation not valid: Control 'FormName'
    '   accessed from a thread other than the thread it was created on.'

    ' BUT...

    ' If we write something like this:

    _BGWorker.ReportProgress(-1, New Action(
    Sub()
        '...and put that line inside this action:
        Me.Text = "Background worker is changing form title! :-O"

        ' Then NO PROBLEM! UI-interactive code MAGIC!

        ' Well, it's not magic... This works because this code is not executed here immediately.
        ' It is an Action Delegate: https://learn.microsoft.com/en-us/dotnet/api/system.action-1?view=net-7.0
        ' You can write ANY UI-interactive code inside blocks like this in your BackgroundWorkers
        ' and they will all execute on the main thread, because the Action delegate is being 
        ' shipped to the ProgressChanged event handler on the main Form thread.

        'TODO: Add your other UI code...

    End Sub))

    ' Now simply repeat the above section every time you wish write UI-interactive
    ' code in your BG workers! SHAZAM!

    '...your background worker code...
End Sub

    


© www.soinside.com 2019 - 2024. All rights reserved.