How to throttle the speed of an event without using Rx Framework

后端 未结 2 1197
闹比i
闹比i 2020-12-14 23:39

I want to throttle the speed of an event, How I can achieve this without using Microsoft Rx framework. I had done this with the help of Rx. But what I am trying is, I need t

相关标签:
2条回答
  • 2020-12-15 00:28

    I believe the following requirements are essential in a 'throttled' event handler:

    • The first event is raised immediately.
    • Subsequent events - which occur within the throttling period - are ignored.
    • The last event to occur during the throttling period is guaranteed to be raised, once the throttling period has expired.

    Considering those requirements, the previously-accepted answer was not satisfactory; it correctly fulfills the first two requirements, but it does not guarantee that the last event will eventually be raised. I think that point is particularly important, because events which are raised with high frequency typically represent 'change of state' and/or 'user requests'; and we always want to receive the last update for changes in state or user interaction.

    In an effort to satisfy all these requirements, I created my own generic "ThrottledEventHandler" class.

    public class ThrottledEventHandler<TArgs>
        where TArgs : EventArgs
    {
        private readonly EventHandler<TArgs> _innerHandler;
        private readonly EventHandler<TArgs> _outerHandler;
        private readonly Timer _throttleTimer;
    
        private readonly object _throttleLock = new object();
        private Action _delayedHandler = null;
    
        public ThrottledEventHandler(EventHandler<TArgs> handler, TimeSpan delay)
        {
            _innerHandler = handler;
            _outerHandler = HandleIncomingEvent;
            _throttleTimer = new Timer(delay.TotalMilliseconds);
            _throttleTimer.Elapsed += Timer_Tick;
        }
    
        private void HandleIncomingEvent(object sender, TArgs args)
        {
            lock (_throttleLock)
            {
                if (_throttleTimer.Enabled)
                {
                    _delayedHandler = () => SendEventToHandler(sender, args);
                }
                else
                {
                    SendEventToHandler(sender, args);
                }
            }
        }
    
        private void SendEventToHandler(object sender, TArgs args)
        {
            if (_innerHandler != null)
            {
                _innerHandler(sender, args);
                _throttleTimer.Start();
            }
        }
    
        private void Timer_Tick(object sender, EventArgs args)
        {
            lock (_throttleLock)
            {
                _throttleTimer.Stop();
                if (_delayedHandler != null)
                {
                    _delayedHandler();
                    _delayedHandler = null;
                }
            }
        }
    
        public static implicit operator EventHandler<TArgs>(ThrottledEventHandler<TArgs> throttledHandler)
        {
            return throttledHandler._outerHandler;
        }
    }
    

    Usage looks something like this:

    myObject.MyFrequentlyRaisedEvent += new ThrottledEventHandler(MyActualEventHandler, TimeSpan.FromMilliseconds(50));
    
    0 讨论(0)
  • 2020-12-15 00:38

    This works, if your event is of type EventHandler<EventArgs> for example. It creates a wrapper for your event handler that is throttled:

    private EventHandler<EventArgs> CreateThrottledEventHandler(
        EventHandler<EventArgs> handler, 
        TimeSpan throttle)
    {   
        bool throttling = false;
        return (s,e) =>
        {
            if(throttling) return;              
            handler(s,e);
            throttling = true;
            Task.Delay(throttle).ContinueWith(_ => throttling = false);
        };
    }
    

    Attach like this:

    this.SomeEvent += CreateThrottledEventHandler(
        (s,e) => Console.WriteLine("I am throttled!"),
        TimeSpan.FromSeconds(5));
    

    Although, you should store the handler returned from CreateThrottledEventHandler if you need to unwire it with -= later.

    0 讨论(0)
提交回复
热议问题