Simple way to throttle parts of your Asp.Net web app
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:
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 (new Throttle()) {
... do my expensive operation here ...
}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);
}
}
}