Is there a better way to throttle high bandwidth jobs?

I created a simple class that shows what I'm trying to do without noise. Feel free to bash in my code. That is why I posted it here.

public class Throttled : IDisposable { private readonly Action work; private readonly Func<bool> stop; private readonly ManualResetEvent continueProcessing; private readonly Timer throttleTimer; private readonly int throttlePeriod; private readonly int throttleLimit; private int totalProcessed; public Throttled(Action work, Func<bool> stop, int throttlePeriod, int throttleLimit) { this.work = work; this.stop = stop; this.throttlePeriod = throttlePeriod; this.throttleLimit = throttleLimit; continueProcessing = new ManualResetEvent(true); throttleTimer = new Timer(ThrottleUpdate, null, throttlePeriod, throttlePeriod); } public void Dispose() { throttleTimer.Dispose(); ((IDisposable)continueProcessing).Dispose(); } public void Execute() { while (!stop()) { if (Interlocked.Increment(ref totalProcessed) > throttleLimit) { lock (continueProcessing) { continueProcessing.Reset(); } if (!continueProcessing.WaitOne(throttlePeriod)) { throw new TimeoutException(); } } work(); } } private void ThrottleUpdate(object state) { Interlocked.Exchange(ref totalProcessed, 0); lock (continueProcessing) { continueProcessing.Set(); } } } 

Last code

 public class Throttled { private readonly Func<bool> work; private readonly ThrottleSettings settings; private readonly Stopwatch stopwatch; private int totalProcessed; public Throttled(Func<bool> work, ThrottleSettings settings) { this.work = work; this.settings = settings; stopwatch = new Stopwatch(); } private void Execute() { stopwatch.Start(); while (work()) { if (++totalProcessed > settings.Limit) { var timeLeft = (int)(settings.Period - stopwatch.ElapsedMilliseconds); if (timeLeft > 0) { Thread.Sleep(timeLeft); } totalProcessed = 0; stopwatch.Reset(); stopwatch.Start(); } } } } 
+4
source share
2 answers

First of all, I will completely get rid of the control thread, because its work can be easily done before calling work() .

Then I will make the workflow different from the main thread, thereby unlocking the main thread for other tasks. Then I would add a function to cancel processing that might have set the flag marked by the workflow.

Edit:
According to the comments, our goal is to limit the number of work() calls during each throttlePeriod tag. We can do this better by noting the time in the stopwatch by comparing it after throttleLimit work operations and the remaining time remaining. Thus, we again do not need a timer thread.

Edit: (deleted, it was incorrect)
Edit:
We can even do some balancing: being within the throttlePeriod , we calculate how much time work() did, so we can estimate hw a lot of time, all the remaining work() are going to take, and wait between each two work() equal shares time remaining. This will force us not to execute all work() very quickly at the beginning of the allocated period, possibly blocking the database.

+1
source

Why throttle? and why did Sleep (), when you can put a thread at a lower priority and make it absorb ALL unused CPU cycles, did their job as quickly as possible without interrupting the work with a higher priority?

In fact, why not put all non-user threads in the lower thread priority so that your application remains generally responsive?

The only caveat here is that if you are doing IO disk access, you need to throttle it so that everything else works smoothly.

0
source

Source: https://habr.com/ru/post/1303866/


All Articles