我需要有一个事件处理程序才能在执行等待操作的UWP项目中拖动一个元素。
因此,我需要将事件处理程序标记为异步:
myElement.PointerMoved += OnPointerMoved;
public async void OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
await MyOperationAsync();
}
结果,我发现UWP框架调用了OnPointerMoved,即使先前的执行没有完成(可以预见的,因为您不能等待异步void方法...)。
我正在寻找一种解决方案,以确保按顺序调用事件处理程序中的代码(即,在上一个实际完成之后,应执行OnPointerMoved的下一次执行)。
有人对此有一个优雅的解决方案吗?
答案 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线程上返回执行后将对其进行处理。一旦没有其他要处理的内容,MyOperationAsync
以while
为空并且_operationQueue
设置为_isProcessing
结束-
准备另一个事件来临。
我认为这种解决方案在简单的情况下就足够了,并且实际上应该是安全的,除非有人从非UI线程调用false
。
您甚至可以在方法开始时进行检查:
EnqueueOperationAsync
注意:尽管从我的测试来看,逻辑似乎很可靠,但我还是希望与其他人一起检查这一点:-)