【问题标题】:UWP: How to make sure async events are processed sequentiallyUWP:如何确保按顺序处理异步事件
【发布时间】:2019-01-25 17:54:07
【问题描述】:

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

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

myElement.PointerMoved += OnPointerMoved;

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

结果,我发现UWP框架调用了OnPointerMoved,即使之前的执行没有完成(这是可以预见的,因为你不能等待一个async void方法......)。

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

有没有人为此提供优雅的解决方案?

【问题讨论】:

  • 不,你可以awaitasync void,只是调用你的处理程序的框架不能。
  • 没有看到 MyOperationAsync 究竟做了什么,很难说。也许您应该将所有操作排队并让您自己的线程/空闲计时器处理它们?
  • @MickyD 我无法使用另一个线程,因为我需要访问 UiElements
  • 然后使用空闲调度计时器

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


【解决方案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执行期间,这种情况下_isProcessing必须是true,所以新到的操作将仅被排队,并且将在MyOperationAsync 完成并在 UI 线程上返回执行后进行处理。一旦没有什么要处理的,while_operationQueue 为空而终止,_isProcessing 设置为 false - 准备好迎接另一场活动。

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

您甚至可以在方法的开头检查这一点:

if (CoreWindow.GetForCurrentThread().Dispatcher.HasThreadAccess)
   throw new InvalidOperationException(
         "This method must be called from the UI thread");

注意:尽管从我的测试来看逻辑似乎是可靠的,但我宁愿与其他人一起检查:-)

【讨论】:

  • 太棒了:-)!谢谢@StephenCleary
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-20
  • 2023-03-15
  • 2023-03-14
  • 1970-01-01
相关资源
最近更新 更多