На самом деле это типичная проблема производителя / потребителя , которая имеет множество решений, распространяющихся по сети.
Однако в вашем случае ситуация немного проще благодаря тому факту, чточто событие всегда запускается в потоке пользовательского интерфейса.Поэтому вместо немедленного запуска операции вы можете создать промежуточный метод, который будет ставить в очередь действия:
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
вызывается только из потока пользовательского интерфейса (что имеет место, еслизапускается OnPointerMoved
), это должно работать именно так, как вам нужно, благодаря тому факту, что существует только один поток пользовательского интерфейса и из-за автоматического возврата в поток пользовательского интерфейса await
, единственное место, где метод EnqueueOperationAsync
может оставитьПоток пользовательского интерфейса находится во время выполнения MyOperationAsync
, в этом случае _isProcessing
должен быть true
, поэтому вновь поступающая операция будет поставлена в очередь только и будет обработана после завершения MyOperationAsync
и возврата к выполнению в потоке пользовательского интерфейса.Если обработать больше нечего, while
завершается, когда _operationQueue
становится пустым, а _isProcessing
устанавливается на false
- готовность к следующему событию.
Я думаю, что этого решения достаточно в простомслучаи и должны действительно быть безопасными, если кто-то не вызывает EnqueueOperationAsync
из потока, не являющегося пользовательским интерфейсом.
Вы можете даже проверить это в начале метода:
if (CoreWindow.GetForCurrentThread().Dispatcher.HasThreadAccess)
throw new InvalidOperationException(
"This method must be called from the UI thread");
Примечание: Несмотря на то, что логика в моих тестах выглядит убедительной, я лучше тоже проверю это с кем-то еще: -)