Parameter Based Lock

Suppose I have this method:

void Foo(int bar) { // do stuff } 

Here is the behavior that I want Foo have:

  • If thread 1 calls Foo(1) and thread 2 calls Foo(2) , both threads can start simultaneously.

  • If thread 1 calls Foo(1) and thread 2 calls Foo(1) , both threads cannot run at the same time. p>

Is there a good standard way in .net to specify this type of behavior? I have a solution that uses an object dictionary to lock, but it seems dirty.

+8
c # concurrency
source share
5 answers

Use a dictionary that provides different lock objects for different arguments. Set up the dictionary when you instantiate the base object (or statically, if applicable):

 var locks = new Dictionary<int, object>() { {1, new Object()}, {2, new Object()}, … }; 

And then use it inside your method:

 void Foo(int bar) { lock (locks[bar]) { … } } 

I would not say that this solution is erratic, on the contrary: the provision of shallow literacy locks is commendable, and since value type locks do not work in .NET, having a mapping is an obvious solution.

Be careful: the above only works until the dictionary is changed and read at the same time. Therefore, it is best to use a read-only dictionary after setting it up.

+6
source share

Bottom line: you cannot block value types.

The dictionary you use is the best approach I can think of. It is kludgey, but it works.

Personally, I was looking for an architectural solution that makes locking unnecessary, but I don't know enough about your system to give you pointers.

+3
source share
 private static readonly Dictionary<int, object> dictionary = new Dictionary<int, object>(); void Foo(int bar) { object bodyLock; lock (dictionary) { if (dictionary.ContainsKey(bar)) { bodyLock = dictionary[bar]; } else { bodyLock = new object(); dictionary.Add(bar, bodyLock); } } lock (bodyLock) { // Processing logic here } } 

A static dictionary is used to ensure that even if a method is called from different objects, it will execute synchronously for the same parameter.

-one
source share

Using a dictionary is not enough; you must use a "ConcurrentDictionary" or implement a data structure that supports multi-threaded access.

-2
source share

Creating a dictionary <> so that you can lock the value seems to me redundant. I got this working using a string. There are people (like John Skeet) who don’t like this approach (and for good reason - see this post: Can I use a string as a lock object? )

But I have a way to mitigate these problems: put the line "on the fly" and combine it with a unique identifier.

 // you should insert your own guid here string lockIdentifier = "a8ef3042-e866-4667-8673-6e2268d5ab8e"; public void Foo(int bar) { lock (string.Intern(string.Format("{0}-{1}", lockIdentifier, bar))) { // do stuff } } 

What happens is that different values ​​are stored in the start pool (which crosses the boundaries of the AppDomain). Adding a lockIdentifier to a string ensures that the string does not conflict with interned strings used in other applications, i.e. the lock will only take effect in your own application.

Thus, the internal pool will return a reference to the interned string - this is normal for blocking.

-2
source share

All Articles