Недопустимая межпоточная операция — VB.NET

Я использую vb.net, и в моей программе я получаю эту ошибку «недопустимая межпоточная операция», когда я запускаю свой фоновый рабочий процесс, который сделает это текстовое поле включенным. Моя основная подпрограмма сначала изменит значение enable на false, а когда фоновый рабочий запустится, он вернет его обратно, а затем выйдет. Почему мне выдает ошибку? К вашему сведению: для этого есть еще код, но я не хочу делать его еще более запутанным...

Вот трассировка стека:

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."}

Может кто-нибудь, пожалуйста, помогите мне!

Спасибо,

Кевин


person lab12    schedule 10.02.2010    source источник
comment
Я думаю, что сообщение об ошибке говорит само за себя.   -  person Anon.    schedule 11.02.2010


Ответы (8)


Цель класса BackgroundWorker — выполнять работу в потоке не с графическим интерфейсом, в то время как графический интерфейс остается отзывчивым. Если вы не установите Control.CheckForIllegalCrossThreadCalls в false (чего вам не следует делать) или не используете Invoke, как предлагается в других ответах (что я также не рекомендовал бы), вы получите исключение недопустимой операции с несколькими потоками.

Если вы хотите, чтобы «вещи», связанные с графическим интерфейсом, происходили во время работы вашего BackgroundWorker, я обычно рекомендую использовать метод BackgroundWorker.ReportProgress и прикреплять соответствующий обработчик к событию BackgroundWorker.ProgressChanged. Если вы хотите, чтобы что-то в графическом интерфейсе происходило после завершения события BackgroundWorker, просто прикрепите свой обработчик для обновления графического интерфейса к событию BackgroundWorker.RunWorkerCompleted.

person Dan Tao    schedule 10.02.2010
comment
+1. Использование backgroundworker.reportprogress помогло мне обновить мой графический интерфейс, пока поток находится в работе. - person Codemunkeee; 29.10.2014

Введите следующий код в подпункт Form1_Load (или любую другую форму):

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False

Он устраняет все проблемы с заблокированными межпотоковыми операциями.

person alex    schedule 06.08.2012
comment
Обратите внимание, что этот ответ плохой, как объяснено в Безопасно ли просто установить для CheckForIllegalCrossThreadCalls значение false, чтобы избежать ошибок перекрестных потоков во время отладки?. Пожалуйста, не используйте его, а исправьте ошибки в коде (т. е. создайте правильных делегатов). - person AStopher; 14.08.2016

Лучший способ сделать это в VB.NET - использовать Extension, это делает очень красивый код для многопоточных вызовов управления графическим интерфейсом.

Просто добавьте эту строку кода в любой модуль, который у вас есть.

<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"))
person SSpoke    schedule 17.05.2014

Где именно вы устанавливаете свойство 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)
person Fredrik Mörk    schedule 10.02.2010

Вы не можете напрямую установить свойство элемента управления, которое находится в потоке пользовательского интерфейса, из другого потока. Это можно сделать, вот пример из 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
person jac    schedule 10.02.2010
comment
Итак, где я должен поместить часть textbox1.enabled = true? - person lab12; 11.02.2010

Ваш Form_Load(), пожалуйста, напишите ниже часть кода. Все ваши проблемы будут решены.

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

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
person AlperDiktas    schedule 14.01.2012

Предлагаем использовать AutomationPeer.

Например, приведенный ниже код вызывает кнопку «Выполнить», когда я нажимаю клавишу F5. Точно так же, если вы хотите, чтобы поток или фоновый рабочий вызывал событие или функцию, вы можете использовать узел автоматизации. В вашем случае вместо кнопки (которую я использовал здесь) вы можете использовать текстовое поле с соответствующим свойством для вызова.

'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

С уважением Р.В.

person Venkat    schedule 17.08.2014

person    schedule
comment
Пожалуйста, добавьте дополнительные пояснения. - person rghome; 19.12.2016