对于频繁的事件等待太慢;异步方法可以忽略取消吗?

本文关键字:取消 异步方法 于频繁 事件 等待 | 更新日期: 2023-09-27 18:14:35

我有一个需要对SizeChanged事件作出反应的WPF窗口。但是,它应该只在没有进一步的SizeChanged事件时执行处理,时间为500 ms(类似于BindingBase.Delay提供的行为)。

private CancellationTokenSource lastCts;
private async void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
    if (lastCts != null)
        lastCts.Cancel();
    lastCts = new CancellationTokenSource();
    try
    {
        await Task.Delay(500, lastCts.Token);
    }
    catch (OperationCanceledException)
    {
        return;
    }
    myTextBox.Text = string.Format("({0}, {1})", this.Width, this.Height);            
}

然而,我注意到,当在调试模式下编译为x64时,这段代码导致UI在调整大小时开始滞后;在重新绘制窗口时有明显的延迟。我假设这是由于OperationCanceledException在UI线程上被序列化、抛出和捕获。下面的代码消除了这个问题:

    Task.Delay(500, lastCts.Token).ContinueWith(
        _ =>
        {
            myTextBox.Text = string.Format("({0},{1})", this.Width, this.Height);
        },
        lastCts.Token,
        TaskContinuationOptions.NotOnCanceled,
        TaskScheduler.FromCurrentSynchronizationContext());

我的问题是:是否有一种干净的方式配置异步方法,仅在等待的任务未被取消时恢复UI线程上的处理?或者这是一个边界情况,由于SizeChanged事件的频率,我们不应该使用await,而是恢复到提供更多控制的旧ContinueWith模式(如TaskContinuationOptions.NotOnCanceled)?

对于频繁的事件等待太慢;异步方法可以忽略取消吗?

它应该只在500 ms时间内没有进一步的SizeChanged事件时才执行处理

只要你有一个"时间"的要求,这是一个非常清楚的迹象,你应该使用Rx。像这样的代码应该可以工作:

Observable.FromEventPattern<SizeChangedEventHandler, SizeChangedEventArgs>(h => SizeChanged += h, h => SizeChanged -= h)
    .Throttle(TimeSpan.FromMilliseconds(500))
    .ObserveOn(SynchronizationContext.Current)
    .Subscribe(_ =>
    {
       myTextBox.Text = string.Format("({0}, {1})", this.Width, this.Height);
    });