2

我有一个在System.Net.Sockets.NetworkStream.BeginRead完成时异步调用的方法。

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

在那个回调方法中,我需要与 UI 线程交互。

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

这会在方法完成后导致异常。(执行 End Sub 时)

Undo 操作遇到的上下文与在相应的 Set 操作中应用的上下文不同。可能的原因是在线程上设置了上下文并且未还原(撤消)。

那么如何通过回调方法与 UI 线程交互呢?我究竟做错了什么?

4

4 回答 4

2

您必须在 frmMain 对象上使用 Invoke 或 BeginInvoke 将消息(委托)排入队列以在 UI 线程上执行。

这是我在 C# 中的做法。

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

还要检查这个Invoke 类型及其用途列表

于 2009-11-15T06:22:45.333 回答
1

特拉维斯是正确的。Windows 窗体应用程序是单线程的,您无法从任何其他线程访问 UI。您需要使用 BeginInvoke 编组对 UI 线程的调用。

请参阅:http: //msdn.microsoft.com/en-us/library/0b1bf3y3.aspx

于 2009-11-15T06:27:52.660 回答
1

您需要让 UI 线程调用 frmMain.refreshStats 方法。当然,有很多方法可以使用 Control.InvokeRequired 属性和 Control.Invoke(MSDN 文档)来执行此操作。

您可以让“EndAsync”方法使方法调用 UI 线程安全,或者让 refreshStats 方法检查线程安全(使用 Control.InvokeRequired)。

EndAsync UI 线程安全将是这样的:

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 方法检查它是否需要在 UI 线程下调用自己:

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
于 2009-11-16T06:39:15.790 回答
0

我找到了解决方案(实际上是解决方法!)当我在 UI 线程上进行交互甚至从表单中读取属性时遇到的重复出现的 InvalidContextException 错误。

在通过异步回调方法与 UI 线程交互之前和之后,我必须备份和恢复执行上下文。然后异常会像它出现时一样神秘地消失,你可以读/写属性,调用方法,基本上可以用 UI 线程做任何你喜欢的事情,从你的异步回调同步,而不必使用委托或调用!

这个异常实际上是 .NET 框架本身的一个低级错误。请参阅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
于 2009-11-15T07:42:38.487 回答