Good day. He is interested in how to realize the triggering of an event not more often than for example 2 seconds. those. a person hammers a button, an event is signed to the button, it is necessary that when an event triggers, a timer starts counting the time interval during which the event does not work. It seems that the task is not complicated, but with a timer you get a lot of code (start, test condition, reset timer). can this be organized in async / await?

Thank you in advance !!

  • one
    Anti-chatter? Cool plus :) - alexis031182

2 answers 2

Easy peasy.

bool isClickHot = false; void OnClick(...) { if (isClickHot) return; BlockClick(); // ваш обработчик } async void BlockClick() { isClickHot = true; await Task.Delay(2000); isClickHot = false; } 
  • Thank!!!!!!!!!! - Aldmi
  • @Aldmi: Please! - VladD
  • An interesting solution :) - nurchi

The variant with await Task.Delay is working, but it creates an extra timer and uses a pool of threads - while this particular task is quite simply solved without it.

This is how it is done using Stopwatch :

 private Stopwatch lastEvent; public void OnEvent() { if (lastEvent != null && lastEvent.ElapsedMilliseconds < 2000) return; lastEvent = Stopwatch.StartNew(); //... } 

This can also be done with Environment.TickCount - but there the code is a little more complicated; in addition, TickCount loops after about 49 days of uptime (and a simple comparison begins to lie after 24 days).

I don’t recommend using DateTime.Now for measuring time intervals - the user will transfer the system date and everything will hang.


PS If such an approach suddenly seems “not expressive enough” - you can make a service structure to enhance this very expressiveness:

 public struct DebounceHelper { private TimeSpan delay; private Stopwatch lastEvent; public DebounceHelper(TimeSpan delay) { this.delay = delay; this.lastEvent = null; } public bool Test() { if (lastEvent != null && lastEvent.Elapsed < delay) return false; lastEvent = Stopwatch.StartNew(); return true; } } 

Now the main part of the code will be even more beautiful:

 private DebounceHelper eventDebounceHelper = new DebounceHelper(TimeSpan.FromSeconds(2)); public void OnEvent() { if (!eventDebounceHelper.Test()) return; //... } 
  • And why Task.Delay uses a pool of threads? It seems not to use? - VladD 5:53 pm
  • one
    @VladD Task.Delay uses Timer , which can use a pool of threads if several timers were triggered simultaneously. Plus, something tells me that for the so-called. AppDomainTimer inside the CLR uses a timer from WinAPI - and they , again, without a special flag are executed in the pool threads. - Pavel Mayorov 6:07 pm
  • @VladD + in CoreCLR AppDomainTimer uses a ThreadpoolMgr inside that uses a QueueUserWorkItem for any timers. Although WinAPI timers are not used there, I did not guess with them. - Pavel Mayorov pm
  • Well, but even so, the thread is not busy all the time, but only to call a callback, as I understand it? - VladD
  • one
    @Sergey is it since when is it there? .. In addition, the algorithm "memorized time" is very vulnerable to translating the system clock back. - Pavel Mayorov