.NET async / await событие debouncer / throttler - PullRequest
0 голосов
/ 11 марта 2019

У меня возникают проблемы с представлением, как будет выглядеть ограничитель событий (он же debouncer) в .NET с async/await.

Рассмотрим следующий код.

/// <summary>
/// A local, in-memory event throttler/debuouncer.
/// </summary>
public class EventThrottler
{
    private TimeSpan _delay = TimeSpan.FromSeconds(5);

    /// <summary>
    /// Begin a timer to release callers of "AwaitEvent".
    /// If a timer has already begun, push it back for the length of 5 seconds.
    /// This method should not block.
    /// </summary>
    public void TriggerEvent()
    {
    }

    /// <summary>
    /// Multiple people can await.
    /// Callers will be released exactly 5 seconds after the last call to "TriggerEvent".
    /// If no call is ever made to "TriggerEvent", consumers of "AwaitEvent" will wait, indefinitely (or until CancellationToken is triggered).
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    public Task AwaitEvent(CancellationToken token)
    {
        return Task.CompletedTask;
    }
}

Какой подход я должен использовать здесь?

A ManualResetEvent, что все абоненты на AwaitEvent могут ждать? Затем System.Timers.Timer, который сбрасывается после каждого вызова на TriggerEvent, что в конечном итоге освобождает событие?

1 Ответ

0 голосов
/ 11 марта 2019

Я нашел решение.

public class EventThrottler
{
    private object _lock = new object();
    private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
    private Timer _timer;

    public EventThrottler(TimeSpan delay)
    {
        _timer = new Timer();
        _timer.Interval = delay.TotalMilliseconds;
        _timer.AutoReset = false;
        _timer.Elapsed += OnTimerElapsed;
    }

    public void TriggerEvent()
    {
        _timer.Stop();
        _timer.Start();
    }

    public async Task AwaitEvent(CancellationToken token)
    {
        CancellationTokenSource tokenSource;

        lock (_lock)
        {
            if (_cancellationTokenSource == null)
            {
                _cancellationTokenSource = new CancellationTokenSource();
            }

            tokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, _cancellationTokenSource.Token);
        }

        try
        {
            await Task.Delay(Timeout.Infinite, tokenSource.Token);
        }
        catch (TaskCanceledException ex)
        {
            if (token.IsCancellationRequested)
            {
                throw;
            }
        }
    }

    private void OnTimerElapsed(object sender, ElapsedEventArgs e)
    {
        CancellationTokenSource tokenSource = null;

        lock (_lock)
        {
            if (_cancellationTokenSource != null)
            {
                tokenSource = _cancellationTokenSource;
                _cancellationTokenSource = null;
            }
        }

        if (tokenSource != null)
        {
            tokenSource.Cancel();
            tokenSource.Dispose();
        }
    }
}
...