Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Named Lock Collection in C#?

I have multiple threads writing data to a common source, and I would like two threads to block each other if and only if they are touching the same piece of data.

It would be nice to have a way to lock specifically on an arbitrary key:

string id = GetNextId();
AquireLock(id);
try
{
    DoDangerousThing();
}
finally
{
    ReleaseLock(id);
}

If nobody else is trying to lock the same key, I would expect they would be able to run concurrently.

I could achieve this with a simple dictionary of mutexes, but I would need to worry about evicting old, unused locks and that could become a problem if the set grows too large.

Is there an existing implementation of this type of locking pattern.

like image 485
captncraig Avatar asked Nov 03 '14 22:11

captncraig


2 Answers

You can try using a ConcurrentDictionary<string, object> to create named object instances. When you need a new lock instance (that you haven't used before), you can add it to the dictionary (adding is an atomic operation through GetOrAdd) and then all threads can share the same named object once you pull it from the dictionary, based on your data.

For example:

// Create a global lock map for your lock instances.
public static ConcurrentDictionary<string, object> GlobalLockMap =
    new ConcurrentDictionary<string, object> ();

// ...

var oLockInstance = GlobalLockMap.GetOrAdd ( "lock name", x => new object () );

if (oLockInstance == null)
{
    // handle error
}

lock (oLockInstance)
{
    // do work
}
like image 121
xxbbcc Avatar answered Oct 15 '22 09:10

xxbbcc


The lock keyword (MSDN) already does this.

When you lock, you pass the object to lock on:

lock (myLockObject)
{
}

This uses the Monitor class with the specific object to synchronize any threads using lock on the same object.

Since string literals are "interned" – that is, they are cached for reuse so that every literal with the same value is in fact the same object – you can also do this for strings:

lock ("TestString")
{
}

Since you aren't dealing with string literals you could intern the strings you read as described in: C#: Strings with same contents.

It would even work if the reference used was copied (directly or indirectly) from an interned string (literal or explicitly interned). But I wouldn't recommend it. This is very fragile and can lead to hard-to-debug problems, due to the ease with which new instances of a string having the same value as an interned string can be created.

A lock will only block if something else has entered the locked section on the same object. Thus, no need to keep a dictionary around, just the applicable lock objects.

Realistically though, you'll need to maintain a ConcurrentDictionary or similar to allow your objects to access the appropriate lock object.

like image 35
BradleyDotNET Avatar answered Oct 15 '22 07:10

BradleyDotNET