我需要一个事件处理程序来拖动UWP项目中执行等待操作的元素。
因此,我需要将我的事件处理程序标记为async:
myElement.PointerMoved += OnPointerMoved;
public async void OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
await MyOperationAsync();
}
因此,我发现即使之前的执行没有完成,OnPointerMoved也会被UWP框架调用(这是可以预见的,因为你不能等待异步void方法…)
我正在寻找一种解决方案,以确保事件处理程序中的代码按顺序调用(即,OnPointerMoved的下一次执行应该在上一次实际完成之后进行)。
有人对此有一个优雅的解决方案吗?
这实际上是一个常见的生产者/消费者问题的例子,该问题有许多解决方案在网上流传。
然而,在您的情况下,由于事件总是在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");
注意:尽管我的测试逻辑似乎很可靠,但我宁愿与其他人一起检查:-)