I've read a lot of comments mentioning that locking isn't safe for web applications, but, other than web farms, I haven't seen any explanations of why. I would be interested in hearing the arguments against it.
I have a similar need, though I'm caching re-sized images on the hard drive (which is obviously a local action so a web farm scenario isn't an issue).
Here is a redone version of what @Configurator posted. It includes a couple features that @Configurator didn't include:
- Unlocking: Ensures the list doesn't grow unreasonably large (we have millions of photos and we can have many different sizes for each).
- Generic: Allows locking based on different data types (such as int or string).
Here's the code...
/// <summary>
/// Provides a way to lock a resource based on a value (such as an ID or path).
/// </summary>
public class Synchronizer<T>
{
private Dictionary<T, SyncLock> mLocks = new Dictionary<T, SyncLock>();
private object mLock = new object();
/// <summary>
/// Returns an object that can be used in a lock statement. Ex: lock(MySync.Lock(MyValue)) { ... }
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public SyncLock Lock(T value)
{
lock (mLock)
{
SyncLock theLock;
if (mLocks.TryGetValue(value, out theLock))
return theLock;
theLock = new SyncLock(value, this);
mLocks.Add(value, theLock);
return theLock;
}
}
/// <summary>
/// Unlocks the object. Called from Lock.Dispose.
/// </summary>
/// <param name="theLock"></param>
public void Unlock(SyncLock theLock)
{
mLocks.Remove(theLock.Value);
}
/// <summary>
/// Represents a lock for the Synchronizer class.
/// </summary>
public class SyncLock
: IDisposable
{
/// <summary>
/// This class should only be instantiated from the Synchronizer class.
/// </summary>
/// <param name="value"></param>
/// <param name="sync"></param>
internal SyncLock(T value, Synchronizer<T> sync)
{
Value = value;
Sync = sync;
}
/// <summary>
/// Makes sure the lock is removed.
/// </summary>
public void Dispose()
{
Sync.Unlock(this);
}
/// <summary>
/// Gets the value that this lock is based on.
/// </summary>
public T Value { get; private set; }
/// <summary>
/// Gets the synchronizer this lock was created from.
/// </summary>
private Synchronizer<T> Sync { get; set; }
}
}
Here's how you can use it...
public static readonly Synchronizer<int> sPostSync = new Synchronizer<int>();
....
using(var theLock = sPostSync.Lock(myID))
lock (theLock)
{
...
}