我正在使用 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."}
有人可以帮帮我吗!
谢谢,
凯文
BackgroundWorker
类的目的是在非GUI线程上执行工作,同时GUI保持响应。除非您将 Control.CheckForIllegalCrossThreadCalls
设置为 false
(您不应该这样做),或者按照其他答案中的建议使用 Invoke
(我也不推荐),否则您将获得非法交叉线程操作异常。
如果您希望在 BackgroundWorker
运行时发生与 GUI 相关的“事情”,我通常建议使用
BackgroundWorker.ReportProgress
方法并将适当的处理程序附加到 BackgroundWorker.ProgressChanged
事件。如果您希望在 BackgroundWorker
finished后 GUI 上发生某些事情,则只需附加处理程序即可将 GUI 更新到
BackgroundWorker.RunWorkerCompleted
事件。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)
Form1_Load
(或任何您的表单)子中键入以下代码:
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
它修复了所有阻塞跨线程操作的问题。
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"))
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
'## crossed-thread parts will not be controlled by this option...
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
例如,当我按 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
问候 房车
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