I'd like to use lock objects that are specific to the person I'm updating. In other words, if thread A is updating Person 1, thread B is blocked from also updating Person 1, but thread C is not blocked from updating Person 2.
99% of the time, I don't really need the locks since I'm working with different Person.Id values. In the occasional situations when I am working with the same Person.Id value, I want to lock around some read/write code.
The following code is working as I expect. I'm looking for any "gotcha's" I may have missed, or better ways of accomplishing the same thing. I decided to use the .NET Cache to store the lock objects so I don't need to worry about cleaning them up later (the lock objects are removed from cache when they haven't been used for a certain TimeSpan).
This code provides the object to lock on
public static class PersonLocks
{
private static readonly object CacheLock = new object();
private const string KeyPrefix = "LockForPersonID:";
public static object GetPersonLock(long personId)
{
lock (CacheLock)
{
string key = BuildCacheKey(personId);
object cachedItem = HttpRuntime.Cache[key];
if (cachedItem == null)
{
cachedItem = new object();
HttpRuntime.Cache.Insert(key, cachedItem, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, 5, 0));
}
return cachedItem;
}
}
private static string BuildCacheKey(long personId)
{
return KeyPrefix + personId.ToString();
}
}
This is how I'm using the code
object padlock = PersonLocks.GetPersonLock(person.Id);
lock (padlock)
{
//do read
//do some data mapping
//do write
}
lock (CacheLock)
to get a person lock is expensive. I would use an atomic operation to get the person lock, rather than a lock.ConcurrentDictionary
is the way to go for me. The only downside is that you have to implement expiration yourself. \$\endgroup\$