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 to throttle Map's View changed event based on a time slot. Is it possible to implement the same without using Rx.
I am not allowed to use Rx and I have to keep the binary size as small as possible.
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.
Here is a Throttle
method, inspired by James World's CreateThrottledEventHandler
method, that mimics the behavior of the Rx Throttle/Debounce
operator. It propagates only events that come after a dueTime
period of inactivity. This means that in case the source events are raised in quick succession, with no time gaps larger than dueTime
between them, no event is going to be propagated.
/// <summary>Ignores events that are followed by another event within
/// a specified relative time duration.</summary>
public static EventHandler<TEventArgs> Throttle<TEventArgs>(
EventHandler<TEventArgs> handler,
TimeSpan dueTime)
{
System.Threading.Timer timer = null;
return (s, e) =>
{
var newTimer = new System.Threading.Timer(
_ => handler(s, e), null, dueTime, Timeout.InfiniteTimeSpan);
var previousTimer = Interlocked.Exchange(ref timer, newTimer);
previousTimer?.Dispose();
};
}
Usage example:
public event EventHandler<int> SomeEvent;
//...
this.SomeEvent += Throttle<int>((s, e) =>
{
Console.WriteLine($"Received: {e}");
}, TimeSpan.FromSeconds(1.0));
I believe the following requirements are essential in a 'throttled' event handler:
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));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With