Say you've got a particular piece of code that hogs your cpu/db/whatever on your web app, and you'd like it to be throttled, so that say 10 people could perform a certain operation at once, and any more would get an error message. Thanks to the magic of the 'using' statement and the below class, you can achieve this quite simply. Just wrap your function to be throttled in a using statement like so:
using (new Throttle()) {
  ... do my expensive operation here ...
}
Here's the class below. It uses the asp.net cache to keep track of how many operations are running, and the IDisposable interface so that it knows when the operation has finished:
using System;
using System.Collections;
using System.Web;
using System.Web.Caching;

/// <summary>
/// This is used to throttle anything really, it throws an exception if too many are running simultaneously
/// It works by storing the runs in the http cache
/// Use this with a 'using' statement surrounding your work, eg:
/// 
/// using (new Throttle()) {
///   ... work to be throttled ...
/// }
/// </summary>
public class Throttle : IDisposable
{
  // Used to lock all instances of the throttle, so they don't walk over each other when using the http cache
  static object cacheLock = new object();
  // Max simultaneous users
  const int max = 10;
  // The timeout in seconds at which to consider each run to have finished anyway, eg in case the throttle forgets to clear it
  const int timeout = 30;
  // What prefix to give the names in the cache
  const string cacheEntryNamePrefix = "Throttle";
  // The name of the cache entry used by this particular throttled run
  string thisCacheEntryName;

  /// <summary>
  /// Checks that we haven't hit the throttle, and if so it throws an exception
  /// Then it registers the current run in the cache, to be forgotten when finished
  /// </summary>
  public Throttle() {
    lock (cacheLock) {
      // Check we haven't hit the maximum
      int runners = 0;
      foreach (DictionaryEntry kv in HttpContext.Current.Cache)
        if (kv.Key.ToString().StartsWith(cacheEntryNamePrefix))
          runners++;
      if (runners >= max)
        throw new Exception("Too many people accessing the system right now. Please retry soon.");

      // Register this one into the cache
      thisCacheEntryName = cacheEntryNamePrefix + System.Guid.NewGuid().ToString();
      HttpContext.Current.Cache.Add(thisCacheEntryName, 1, null, DateTime.Now.AddSeconds(timeout), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
    }
  }

  /// <summary>
  /// Removes the entry from the cache
  /// </summary>
  public void Dispose() {
    lock (cacheLock) {
      HttpContext.Current.Cache.Remove(thisCacheEntryName);
    }
  }
}

Thanks for reading! And if you want to get in touch, I'd love to hear from you: chris.hulbert at gmail.

Chris Hulbert

(Comp Sci, Hons - UTS)

Software Developer (Freelancer / Contractor) in Australia.

I have worked at places such as Google, Cochlear, Assembly Payments, News Corp, Fox Sports, NineMSN, FetchTV, Coles, Woolworths, Trust Bank, and Westpac, among others. If you're looking for help developing an iOS app, drop me a line!

Get in touch:
[email protected]
github.com/chrishulbert
linkedin



 Subscribe via RSS