Skip to content

Instantly share code, notes, and snippets.

@tuscen
Forked from MihaZupan/SchedulerVariations.cs
Created May 10, 2018 14:02
Show Gist options
  • Save tuscen/e54737078bda6a341429c763abad0677 to your computer and use it in GitHub Desktop.
Save tuscen/e54737078bda6a341429c763abad0677 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace MihaZupan
{
// Block the caller of WaitOne as long as necesarry
public class RequestScheduler
{
private Queue<ManualResetEvent> RequestQueue = new Queue<ManualResetEvent>();
private object QueueLock = new object();
private int MaxBurst;
private int SafeInterval;
private Timer Timer;
private Stopwatch Stopwatch;
private long TimerIntervals = 0;
private int RequestCount = 0;
public RequestScheduler(int safeInterval = 35, int maxBurst = 25)
{
SafeInterval = safeInterval;
MaxBurst = maxBurst;
Stopwatch = Stopwatch.StartNew();
Timer = new Timer(TimerCallback, null, SafeInterval, Timeout.Infinite);
}
private void TimerCallback(object state)
{
lock (QueueLock)
{
if (RequestQueue.Count > 0)
{
RequestQueue.Dequeue().Set();
}
else if (RequestCount > 0) RequestCount--;
}
TimerIntervals++;
long msElapsed = Stopwatch.ElapsedMilliseconds;
int wait = Math.Max(SafeInterval / 2, (int)((TimerIntervals + 1) * SafeInterval - msElapsed));
Timer.Change(wait, Timeout.Infinite);
}
public void WaitOne()
{
ManualResetEvent mre = null;
lock (QueueLock)
{
if (RequestCount < MaxBurst)
{
RequestCount++;
return;
}
else
{
mre = new ManualResetEvent(false);
RequestQueue.Enqueue(mre);
}
}
mre.WaitOne();
}
}
// Return a ManualResetEvent that is set when safe
public class RequestScheduler2
{
private Queue<ManualResetEvent> RequestQueue = new Queue<ManualResetEvent>();
private object QueueLock = new object();
private int MaxBurst;
private int SafeInterval;
private Timer Timer;
private Stopwatch Stopwatch;
private long TimerIntervals = 0;
private int RequestCount = 0;
public RequestScheduler2(int safeInterval = 35, int maxBurst = 25)
{
SafeInterval = safeInterval;
MaxBurst = maxBurst;
Stopwatch = Stopwatch.StartNew();
Timer = new Timer(TimerCallback, null, SafeInterval, Timeout.Infinite);
}
private void TimerCallback(object state)
{
lock (QueueLock)
{
if (RequestQueue.Count > 0)
{
RequestQueue.Dequeue().Set();
}
else if (RequestCount > 0) RequestCount--;
}
TimerIntervals++;
long msElapsed = Stopwatch.ElapsedMilliseconds;
int wait = Math.Max(SafeInterval / 2, (int)((TimerIntervals + 1) * SafeInterval - msElapsed));
Timer.Change(wait, Timeout.Infinite);
}
public ManualResetEvent Schedule()
{
lock (QueueLock)
{
if (RequestCount < MaxBurst)
{
RequestCount++;
return new ManualResetEvent(true);
}
else
{
var mre = new ManualResetEvent(false);
RequestQueue.Enqueue(mre);
return mre;
}
}
}
}
// Run an action on the thread pool when safe
public class RequestScheduler3
{
private Queue<Action> ActionQueue = new Queue<Action>();
private object QueueLock = new object();
private int MaxBurst;
private int SafeInterval;
private Timer Timer;
private Stopwatch Stopwatch;
private long TimerIntervals = 0;
private int RequestCount = 0;
public RequestScheduler3(int safeInterval = 35, int maxBurst = 25)
{
SafeInterval = safeInterval;
MaxBurst = maxBurst;
Stopwatch = Stopwatch.StartNew();
Timer = new Timer(TimerCallback, null, SafeInterval, Timeout.Infinite);
}
private void TimerCallback(object state)
{
lock (QueueLock)
{
if (ActionQueue.Count > 0)
{
Task.Run(ActionQueue.Dequeue());
}
else if (RequestCount > 0) RequestCount--;
}
TimerIntervals++;
long msElapsed = Stopwatch.ElapsedMilliseconds;
int wait = Math.Max(SafeInterval / 2, (int)((TimerIntervals + 1) * SafeInterval - msElapsed));
Timer.Change(wait, Timeout.Infinite);
}
public void ScheduleActionOnThreadPool(Action action)
{
lock (QueueLock)
{
if (RequestCount < MaxBurst)
{
RequestCount++;
Task.Run(action);
}
else
{
ActionQueue.Enqueue(action);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment