【问题标题】:Using Async CTP Await with events?将 Async CTP Await 与事件一起使用?
【发布时间】:2011-07-19 07:46:29
【问题描述】:

与 Silverlight 5 / Async CTP 相关

我想创建一个异步函数来启动布局更新,然后等待布局更新完成。比如:

    Private Async Function UpdateLayoutRoot() As Task
       LayoutRoot.UpdateLayout()
       Await LayoutRoot.LayoutUpdated  <--- (NOT valid but shows desired outcome)           
    End Function

如何做到这一点?更一般地说,如何使用 Await 来等待现有事件?

【问题讨论】:

  • 这可能是最好使用响应式扩展 (RX) 完成的类型。
  • awaiting a layout to be updated 如果你问我,这似乎是在要求非常奇怪的竞争条件。效果好吗?

标签: vb.net asynchronous silverlight-5.0


【解决方案1】:

实现此目的的一种方法是等待在事件内部设置的TaskCompletionSource。我不懂VB.NET,希望你能从C#中理解:

// The type and value returned by the TaskCompletionSource
// doesn't matter, so I just picked int.
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();

// The delegate sets the TaskCompletionSource -- the result value
// doesn't matter, we only care about setting it.  Keep hold of
// the delegate so it can be removed later.
EventHandler d = (o, e) => { tcs.TrySetResult(1); };

LayoutRoot.LayoutUpdate += d;

try
{
    LayoutRoot.UpdateLayout();
    await tcs.Task;
}
finally
{
    // Don't leak the delegate!
    LayoutRoot.LayoutUpdate -= d;
}

【讨论】:

    【解决方案2】:

    谢谢科里!您对使用 TaskCompletionSource 的建议正是我所需要的。我将 TaskCompletionSource 与 Lucian Wischik's Async CTP specification 结合使用,开发了一对通用 Silverlight 5 类,可用于等待任何 CLR 或路由事件。只需要 Async CTP (AsyncCtpLibrary_Silverlight)(不需要强大的 Rx 库)。这是两个类:

    Public Class AwaitableEvent(Of TResult)
    
        Private eta As EventTaskAwaiter(Of TResult) = Nothing
    
        Sub New(ByVal Sender As Object, ByVal EventName As String)
            eta = New EventTaskAwaiter(Of TResult)
            Dim ei as EventInfo = Sender.GetType.GetEvent(EventName)
            Dim d = [Delegate].CreateDelegate(ei.EventHandlerType, 
                Me, "EventCompletedHandler", True, True)
            ei.AddEventHandler(Sender, d)
        End Sub
    
        Public Function GetAwaiter() As EventTaskAwaiter(Of TResult)
            Return eta
        End Function
    
        Private Sub EventCompletedHandler(ByVal sender As Object, ByVal e As TResult)
            eta.tcs.TrySetResult(e)
        End Sub
    
    End Class
    
    Public Class EventTaskAwaiter(Of TResult)
    
        Friend tcs As New TaskCompletionSource(Of TResult)
    
        Public ReadOnly Property IsCompleted As Boolean
            Get
                Return tcs.Task.IsCompleted
            End Get
        End Property
    
        Sub OnCompleted(r As Action)
            Dim sc = SynchronizationContext.Current
            If sc Is Nothing Then
                tcs.Task.ContinueWith(Sub() r())
            Else
                tcs.Task.ContinueWith(Sub() sc.Post(Sub() r(), Nothing))
            End If
        End Sub
    
        Function GetResult() As TResult
            If tcs.Task.IsCanceled Then Throw New TaskCanceledException(tcs.Task)
            If tcs.Task.IsFaulted Then Throw tcs.Task.Exception.InnerException
            Return tcs.Task.Result
        End Function
    
    End Class
    

    这是一个使用 AwaitableEvent 类来等待鼠标、键盘和计时器事件的示例。

    Private Sub AECaller()
        GetMouseButtonAsync()
        MessageBox.Show("After Await mouse button event")
        GetKeyAsync()
        MessageBox.Show("After Await key event")
        GetTimerAsync()
        MessageBox.Show("After Await timer")
    End Sub
    
    Private Async Sub GetMouseButtonAsync()
        Dim ae As New AwaitableEvent(Of MouseButtonEventArgs)(LayoutRoot, "MouseLeftButtonDown")
        Dim e = Await ae
        MessageBox.Show(String.Format("Clicked {0} at {1},{2}",
                                      e.OriginalSource.ToString,
                                      e.GetPosition(LayoutRoot).X,
                                      e.GetPosition(LayoutRoot).Y))
    End Sub
    
    Private Async Sub GetKeyAsync()
        Dim ae As New AwaitableEvent(Of KeyEventArgs)(LayoutRoot, "KeyDown")
        Dim e = Await ae
        MessageBox.Show(String.Format("Key {0} was pressed", e.Key.ToString))
    End Sub
    
    Private Async Sub GetTimerAsync()
        Dim StopWatch As New DispatcherTimer
        StopWatch.Interval = New TimeSpan(TimeSpan.TicksPerSecond * 6)
        Dim ae As New AwaitableEvent(Of EventArgs)(StopWatch, "Tick")
        StopWatch.Start()
        Await ae
        MessageBox.Show(String.Format("It's {0}seconds later!", StopWatch.Interval.TotalSeconds))
        StopWatch.Stop()
    End Sub
    

    正如预期的那样,Await 语句立即将控制权返回给调用函数。当事件随后完成时,Await 分配结果(适用于被监视事件的事件参数),然后运行异步方法中的剩余代码。

    【讨论】:

      猜你喜欢
      • 2017-11-05
      • 2019-04-15
      • 2014-06-19
      • 2018-01-14
      • 2018-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多