从Async回调方法与UI线程交互?

时间:2009-11-15 05:24:53

标签: c# vb.net multithreading asynchronous ui-thread

我有一个在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时)

  

撤消操作遇到了   上下文与什么不同   已应用于相应的集合中   操作。可能的原因是   在线程上设置了一个上下文   没有恢复(撤消)。

那么如何从回调方法中与UI线程进行交互?我做错了什么?

4 个答案:

答案 0 :(得分:2)

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

以下是我在C#中的表现。

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

同时检查此list of Invoke types and their uses

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

您需要让UI线程调用frmMain.refreshStats方法。使用Control.InvokeRequired属性和Control.Invoke(MSDN Documentation)有很多方法可以做到这一点。

您可以使用“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

答案 3 :(得分:0)

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

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

这个异常实际上是.NET framewok本身的低级错误。请参阅Microsoft Connect bug report,但请注意,它们没有列出功能解决方法。

解决方法:(生产代码)

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