Взаимодействует с потоком пользовательского интерфейса из метода обратного вызова Async? - PullRequest
1 голос
/ 15 ноября 2009

У меня есть метод, который асинхронно вызывается при завершении System.Net.Sockets.NetworkStream.BeginRead .

 skDelegate = New AsyncCallback(AddressOf skDataReceived)
 skStream.BeginRead(skBuffer, 0, 100000, skDelegate, New Object)

В этом методе обратного вызова мне нужно взаимодействовать с потоком пользовательского интерфейса.

Sub skDataReceived(ByVal result As IAsyncResult)
    CType(My.Application.OpenForms.Item("frmMain"), frmMain).refreshStats(d1, d2)
End Sub

Это вызывает исключение после завершения метода. (при выполнении End Sub)

Операция отмены обнаружила контекст, который отличается от того, что был применен в соответствующем наборе операция. Возможная причина в том, что был установлен контекст в потоке и не возвращено (отменено).

Так как мне взаимодействовать с потоком пользовательского интерфейса из метода обратного вызова? Что я делаю не так?

Ответы [ 4 ]

2 голосов
/ 15 ноября 2009

Вы должны использовать Invoke или BeginInvoke для объекта frmMain, чтобы поставить в очередь сообщение (делегат) для выполнения в потоке пользовательского интерфейса.

Вот как я это сделаю в C #.

frmMain.Invoke(() => frmMain.refreshStats(d1, d2));

Также проверьте этот список типов Invoke и их использования .

1 голос
/ 16 ноября 2009

Вам нужно, чтобы поток пользовательского интерфейса вызывал метод frmMain.refreshStats. Существует, конечно, много способов сделать это, используя свойство Control.InvokeRequired и Control.Invoke ( Документация MSDN ).

Вы можете либо сделать так, чтобы метод "EndAsync" сделал поток пользовательского интерфейса безопасным для вызова метода, либо иметь метод refreshStats для проверки безопасности потока (с помощью Control.InvokeRequired).

Потокобезопасный пользовательский интерфейс EndAsync будет выглядеть примерно так:

Public Delegate Sub Method(Of T1, T2)(ByVal arg1 As T1, ByVal arg2 As T2)

Sub skDataReceived(ByVal result As IAsyncResult)
    Dim frmMain As Form = CType(My.Application.OpenForms.Item("frmMain"), frmMain)
    Dim d As Method(Of Object, Object)
'create a generic delegate pointing to the refreshStats method
    d = New Method(Of Object, Object)(AddressOf frmMain.refreshStats)
'invoke the delegate under the UI thread
    frmMain.Invoke(d, New Object() {d1, d2})
End Sub

Или вы можете проверить метод refreshStats, чтобы увидеть, нужно ли ему вызываться из потока пользовательского интерфейса:

Public Delegate Sub Method(Of T1, T2)(ByVal arg1 As T1, ByVal arg2 As T2)

Sub refreshStats(ByVal d1 As Object, ByVal d2 As Object)
'check to see if current thread is the UI thread
    If (Me.InvokeRequired = True) Then
        Dim d As Method(Of Object, Object)
'create a delegate pointing to itself
        d = New Method(Of Object, Object)(AddressOf Me.refreshStats)
'then invoke itself under the UI thread
        Me.Invoke(d, New Object() {d1, d2})
    Else
        'actual code that requires UI thread safety goes here
    End If
End Sub
1 голос
/ 15 ноября 2009

Тревис верен. Приложения Windows формы являются однопоточными, вы не можете получить доступ к интерфейсу из любого другого потока. Вам необходимо выполнить маршалинг вызова в поток пользовательского интерфейса, используя BeginInvoke.

См .: http://msdn.microsoft.com/en-us/library/0b1bf3y3.aspx

0 голосов
/ 15 ноября 2009

Я нашел решение (на самом деле!) Для этой повторяющейся ошибки InvalidContextException, возникающей при каждом взаимодействии или даже чтении свойства из формы в потоке пользовательского интерфейса.

Мне нужно было выполнить резервное копирование и восстановитьконтекст выполнения, до и после взаимодействия с потоком пользовательского интерфейса из моего метода обратного вызова Async.Затем исключение исчезает так же загадочно, как и появилось, и вы можете читать / записывать свойства, вызывать методы и делать в основном все что угодно с потоком пользовательского интерфейса, синхронно с обратным вызовом Async, без использования делегатов или вызовов!

Это исключение на самом деле является ошибкой низкого уровня в самом .NET framewok.См. отчет об ошибке Microsoft Connect , но обратите внимание, что в нем не указаны функциональные обходные пути.

Обходной путь: (рабочий код)

Sub skDataReceived(ByVal result As IAsyncResult)

    // backup the context here
    Dim syncContext As SynchronizationContext = AsyncOperationManager.SynchronizationContext

    // interact with the UI thread
    CType(My.Application.OpenForms.Item("frmMain"), frmMain).refreshStats(d1, d2)

    // restore context.
    AsyncOperationManager.SynchronizationContext = syncContext
End Sub
...