UWP:如何确保异步事件按顺序处理
UWP: How to make sure async events are processed sequentially
我需要一个事件处理程序来在我的 UWP 项目中拖动一个元素来执行等待的操作。
因此我需要将我的事件处理程序标记为异步:
myElement.PointerMoved += OnPointerMoved;
public async void OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
await MyOperationAsync();
}
因此,我发现即使之前的执行未完成,UWP 框架也会调用 OnPointerMoved(这是可以预见的,因为您不能等待 async void 方法...)。
我正在寻找一种解决方案,以确保我的事件处理程序中的代码被顺序调用(即 OnPointerMoved 的下一次执行应该在前一个实际完成之后发生)。
有人对此有优雅的解决方案吗?
这实际上是常见 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));
}
如果您确定 EnqueueOperationAsync
仅从 UI 线程调用(如果它是由 OnPointerMoved
触发的情况),这应该完全按照您的要求工作谢谢事实上,只有一个 UI 线程,并且由于 await
自动返回到 UI 线程,EnqueueOperationAsync
方法唯一可以离开 [=41] 的地方=] 线程正在 MyOperationAsync
执行期间,在这种情况下 _isProcessing
必须是 true
,因此新到达的操作将仅入队,并在 MyOperationAsync
完成后处理并且在 UI 线程上执行 returns。一旦没有更多要处理的内容,while
终止,_operationQueue
为空并且 _isProcessing
设置为 false
-
准备好迎接另一个活动的到来。
我认为这个解决方案在简单情况下就足够了,并且实际上应该是安全的,除非有人从非 UI 线程调用 EnqueueOperationAsync
。
您甚至可以在方法的开头检查这一点:
if (CoreWindow.GetForCurrentThread().Dispatcher.HasThreadAccess)
throw new InvalidOperationException(
"This method must be called from the UI thread");
注意:尽管从我的测试来看逻辑似乎是可靠的,但我还是宁愿与其他人核实一下:-)
我需要一个事件处理程序来在我的 UWP 项目中拖动一个元素来执行等待的操作。
因此我需要将我的事件处理程序标记为异步:
myElement.PointerMoved += OnPointerMoved;
public async void OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
await MyOperationAsync();
}
因此,我发现即使之前的执行未完成,UWP 框架也会调用 OnPointerMoved(这是可以预见的,因为您不能等待 async void 方法...)。
我正在寻找一种解决方案,以确保我的事件处理程序中的代码被顺序调用(即 OnPointerMoved 的下一次执行应该在前一个实际完成之后发生)。
有人对此有优雅的解决方案吗?
这实际上是常见 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));
}
如果您确定 EnqueueOperationAsync
仅从 UI 线程调用(如果它是由 OnPointerMoved
触发的情况),这应该完全按照您的要求工作谢谢事实上,只有一个 UI 线程,并且由于 await
自动返回到 UI 线程,EnqueueOperationAsync
方法唯一可以离开 [=41] 的地方=] 线程正在 MyOperationAsync
执行期间,在这种情况下 _isProcessing
必须是 true
,因此新到达的操作将仅入队,并在 MyOperationAsync
完成后处理并且在 UI 线程上执行 returns。一旦没有更多要处理的内容,while
终止,_operationQueue
为空并且 _isProcessing
设置为 false
-
准备好迎接另一个活动的到来。
我认为这个解决方案在简单情况下就足够了,并且实际上应该是安全的,除非有人从非 UI 线程调用 EnqueueOperationAsync
。
您甚至可以在方法的开头检查这一点:
if (CoreWindow.GetForCurrentThread().Dispatcher.HasThreadAccess)
throw new InvalidOperationException(
"This method must be called from the UI thread");
注意:尽管从我的测试来看逻辑似乎是可靠的,但我还是宁愿与其他人核实一下:-)