Throttle(一定時間リクエストがなければ実行をする)の実装

スライダーなどから大量のリクエストが送られてきたときに、全て処理する必要がない場合、一定時間リクエストがなければ最新の値で処理を行うことがあります。

ReactiveExtensionにもThrottleオペレータは用意されていますので、普段はこれを利用するので問題ないと思います。
GitHub - Reactive-Extensions/Rx.NET: The Reactive Extensions for .NET

今回はThrottle処理を行うクラスを実装してみたいと思います。

Throttleクラス

Delayで処理を遅らせたTaskを利用します。

リクエストが来たら、現在のTaskをキャンセルして新しいTaskを走らせる、とても簡単な内容になっています。

public class Throttle
{
    CancellationTokenSource _cancel = new CancellationTokenSource();
    int _milliseconds;
    public Throttle(int milliseconds) =>
        _milliseconds = milliseconds;
    public void Post(Action action)
    {
        // 現在のTaskをキャンセル
        _cancel.Cancel();
        _cancel = new CancellationTokenSource();

        // 指定時間待ったあと処理を行う
        Task.Delay(_milliseconds, _cancel.Token)
            .ContinueWith(task => action(), _cancel.Token);
    }
}


動作確認

スライダーを動かすと、値がテキストに反映されるアプリを作成してみます。

スライダーを動かしてから1秒以内にまたスライダーを動かす限り、テキストが更新されません。

現状Postは別スレッドで実行されるため、InvokeAsyncでUIスレッドでテキストを更新しています。

f:id:any-programming:20170714181130p:plain

var throttle = new Throttle(1000);

slider.ValueChanged += (s, e) =>
    throttle.Post(() =>
        textblock.Dispatcher.InvokeAsync(() => 
            textblock.Text = ((int)e.NewValue).ToString()));