UWP:如何确保异步事件得到顺序处理

时间:2019-01-24 20:48:34

标签: c# uwp async-await event-handling thread-synchronization

我需要有一个事件处理程序才能在执行等待操作的UWP项目中拖动一个元素。

因此,我需要将事件处理程序标记为异步:

myElement.PointerMoved += OnPointerMoved;

public async void OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
 await MyOperationAsync(); 
}

结果,我发现UWP框架调用了OnPointerMoved,即使先前的执行没有完成(可以预见的,因为您不能等待异步void方法...)。

我正在寻找一种解决方案,以确保按顺序调用事件处理程序中的代码(即,在上一个实际完成之后,应执行OnPointerMoved的下一次执行)。

有人对此有一个优雅的解决方案吗?

1 个答案:

答案 0 :(得分:1)

这实际上是常见的producer/consumer problem的一个实例,它在网络上有许多解决方案。

但是,由于您的情况总是在UI线程上触发,因此情况变得容易一些。因此,您可以创建一个将操作排队的中介方法,而不是立即运行该操作:

private bool _isProcessing = false;
private readonly Queue<PointerPoint> _operationQueue = new Queue<PointerPoint>();

private async Task EnqueueOperationAsync(PointerPoint point)
{
    //using the pointer point as argument of my operation in this example
    _operationQueue.Enqueue(point); 
    if (!_isProcessing)
    {
        _isProcessing = true;
        while (_operationQueue.Count != 0)
        {
            var argument = _operationQueue.Dequeue();
            await MyOperationAsync(argument);
        }
        _isProcessing = false;
    }
}

private async void UIElement_OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
    await EnqueueOperationAsync(e.GetCurrentPoint(this));
}

如果您确保仅从UI线程中调用EnqueueOperationAsync(如果由OnPointerMoved触发则属于这种情况),这要归因于以下事实:只是一个UI线程,并且由于await自动返回到UI线程,因此EnqueueOperationAsync方法可以离开UI线程的唯一位置是在执行MyOperationAsync时,在这种情况下,{{1 }}必须为_isProcessing,因此新到达的操作将仅排队,并且true完成并在UI线程上返回执行后将对其进行处理。一旦没有其他要处理的内容,MyOperationAsyncwhile为空并且_operationQueue设置为_isProcessing结束- 准备另一个事件来临。

我认为这种解决方案在简单的情况下就足够了,并且实际上应该是安全的,除非有人从非UI线程调用false

您甚至可以在方法开始时进行检查:

EnqueueOperationAsync

注意:尽管从我的测试来看,逻辑似乎很可靠,但我还是希望与其他人一起检查这一点:-)