3

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.

1
  • Just to clarify... for the bounty are you looking for an modified RateGate class, or a new class that inherits and overrides/extends some functionality from the RateGate class?
    – Jeff B
    Commented Oct 28, 2013 at 17:47

2 Answers 2

5
+50

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.

Update 3/12/22: the original link to the RateGate class is broken but I found a copy of the class on Github.

3
  • Thanks a lot for your answer Nicolas, it looks promising! Going to check this out shortly. Commented Oct 28, 2013 at 22:00
  • This works perfectly, thanks a lot for the idea of reversing the problem to use 1 occurrence per (1000 / (number per second)) milliseconds. In the end, the code becomes as simple as RateGate.TimeUnitMilliseconds = 1000 / 35; Commented Oct 29, 2013 at 0:11
  • Of course, public int TimeUnitMilliseconds { get; private set; } should also be changed from private set; to set; Commented Oct 29, 2013 at 2:16
1

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.

3
  • Thanks for explaining why this isn't working Jeff; I don't suppose you have any ideas how I'd change the max rate limit, whilst inside an executing RateGate? Commented Oct 25, 2013 at 2:47
  • I'm not sure if it's possible with the current arrangement. If I understand correctly, that semaphore is controlling the flow of things, so if you wipe it out, it'll reset the state. That being said, it may be possible to write a method (or implement the set) so that it would use the current count of the existing semaphore when creating the new one... something like: _semaphore = new SemaphoreSlim(_semaphore.CurrentCount, newOccurrenceValue) but I can't guarantee its behavior.
    – Jeff B
    Commented Oct 25, 2013 at 3:00
  • I've now added a bounty; maybe you'd like to try to figure out if this is possible... Commented Oct 28, 2013 at 1:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.