I am using the concise RateGate class to limit the number of requests I send to a server.
My code looks something like this:
var RateLimit = 35;
using(var RateGate = new RateGate(RateLimit, TimeSpan.FromSeconds(1)))
{
for(var Run = 1; Run <= 50; Run++)
{
for(var Batch = 0; Batch < 200; Batch++)
{
// Do some work, then...
MyClass MyClass;
if(MyClass.RateLimitHit)
{
RateLimit--;
}
RateGate.WaitToProceed();
}
}
}
Inside the if(MyClass.RateLimitHit)
, I need to lower the rate limit by 1. Not just the variable RateLimit
, but the limit running in the actual RateGate
.
In the RateGate class, I see this:
/// <summary>
/// Number of occurrences allowed per unit of time.
/// </summary>
public int Occurrences { get; private set; }
My question is: if I change private set;
to set;
and add RateGate.Occurrences = RateLimit;
after RateLimit--;
will this do what I want?
I tried it, but it looks like the RateGate
continues to execute at a max rate of 35/s.
I wanted to do that too and I found a nice solution by inverting the time and occurences.
What does it mean:
Instead of expressing my problem as "I want N occurences per second", I inversed it as "I want 1 occurence per 1/N seconds". That way, instead of modifying the number of occurences (which will always be 1), I could easily change the time unit. I added this method to the class (you could derive too):
private object _updateTimeUnitLock = new object();
private int nextUpdateTime = 0;
public bool UpdateTimeUnit(TimeSpan timeUnit, TimeSpan dontUpdateBefore)
{
lock (_updateTimeUnitLock)
{
if ((nextUpdateTime == 0) || (nextUpdateTime <= Environment.TickCount))
{
TimeUnitMilliseconds = (int)timeUnit.TotalMilliseconds;
nextUpdateTime = Environment.TickCount + (int)dontUpdateBefore.TotalMilliseconds;
return true;
}
return false;
}
}
Mine had to be threadsafe and I needed a way to prevent changes during certain periods, so on your side you may want to remove the lock and dontUpdateBefore
param, which means you can just set TimeUnitMilliseconds and this value will be picked up at the next timer tick.
Now, to call this, you just need to calculate the new time you want based on the number of occurences you want.
Hope it could fit your needs.
N.
The Occurrences
value gets passed to a semaphore in the constructor as the maximum count, so changing the property will have no effect on the behavior of that instance.
public RateGate(int occurrences, TimeSpan timeUnit)
{
// Snipped all the code that doesn't pertain to this question...
Occurrences = occurrences;
// Create the semaphore, with the number of occurrences as the maximum count.
_semaphore = new SemaphoreSlim(Occurrences, Occurrences);
}
It looks like Occurrences is more of a readonly property that allows you see what was passed in to the constructor.