Fork me on GitHub

Achieving Named Lock / Locker functionality in C# 4.0 9


I was recently writing some code to implement a file system cache for streams and came across an interesting dilemma: how do I lock around a file that is being created by ThreadA until it is ready to be accessed by ThreadB? This is not a classic producer/consumer problem as you might be thinking: Any number of threads within a single app domain could be producing or consuming different parts of the cache (files) simultaneously.

The .NET framework doesn’t appear to provide a way to block multiple threads from accessing the same file short of locking the file it’s self and dealing with exceptions that may occur. I prefer to avoid causing un-necessary exceptions and perhaps have a little fun at the same time so I looked for another solution and actually came up with many…

Full blown lock on the entire resource

Everyone has probably seen the below code more times than they’d like. Lock around an object representing a resource until it’s free and other threads are able to access it. The advantage to this code is that is very straight forward and easy to understand. The problem is that the lock is on the entire resource. This means that anyone wanting to interact with the cache, in my example all files in the cache, are blocked until the entire resource frees up.

  • Pros
    • Easy to understand
  • Cons
    • Slow: Locking on entire resource causes a lot of un-necessary thread blocking on the resource
  • Performance


Named Locking?

From the above example and performance considerations it’s quite apparent that there must be a better solution. In the file system cache example we don’t care if another file in the resource is being accessed, there is no problem with multiple threads working in the cache at the same time, just on the same file. What we need is the ability to lock a single file within the larger cache resource.

Named Mutex Lock

A mutex is similar to the above C# lock with two important distinctions for this use case:

  • A mutex is named by a string. This means for this example I could actually lock on the individual file rather than the resource as a whole by keying on the file path.
  • A mutex is broadcast to the entire operating system so it is suitable for both itra and inter-process communication. If I was running multiple app domains on this cache this would be a very viable solution as long as each process referenced the same key string in the same way.

The major problem with a mutex however is it’s speed: Since it broadcasts to the entire operating system it is much slower than something that can exist entirely in .NET. In fact the Named Mutex was the slowest of all solutions to this problem clocking in at 5.9 times slower than the fastest solution. Lets also admit it looks pretty ugly too!

  • Pros
    • Suitable for interprocess communication (ex: across app domains)
    • Provides named locker functionality
  • Cons
    • Slow: OS level broadcast is much slower than staying in the App Domain
    • Overly verbose and possibly confusing code
  • Performance





Locking on an Interned String

String interning is essentially a way for the .NET CLR to save memory when it comes to string use. As the application is JITing the .NET CLR saves each identical string into a hash table. At run time these ‘different’ strings actually point to the same reference in the hash table so are .Equal() by pointing to the exact same reference in memory.

What makes string interning interesting for the purposes of named locking is that any developer can add to the contents of this cache by using String.Intern() method which both takes and returns the same string. This means that any other time that exact same string is accessed via String.Intern() the same reference in memory will be used which makes it a suitable object to lock on.

Before you get too excited there are a few possible issues with this approach:

If any other code decides to lock on the same string you will likely cause a deadlock at some point in your application because an unexpected lock could be held infinitely blocking one or more threads from running.

The other issue is that the underlying implementation of String.Intern() is very much outside of your control. Furthermore it isn’t really designed for the purposes of locking. Who’s to say that at some future date the CLR might be designed as such to clean up strings from the intern pool to save memory? In fact I couldn’t really find much documentation to guarantee that there is no possible way that a string could leave the intern pool. If this were to happen somehow it’s possible that a lock could be violated compromising the thread safety of our application.

  • Pros
    • Provides named locker functionality and is very fast
    • Simple code and relatively easy to understand
  • Cons
    • Can cause deadlocks if any other code happens to lock on the same string instance
    • Relies on an implementation that is outside of developer control (CLR) and who’s features could possibly change at any time
  • Performance


The NamedLocker class

What if we could have something pretty similar to String.Intern() in performance and readability but with none of the cons? This is what caused me to arrive at the NamedLocker class: and it’s only 30 lines of code at most. As you can see below the core of the NamedLocker class relies on a ConcurrentDictionary (.NET 4.0+ is required). Now it’s certainly feasible to roll your own, Microsoft has kindly provided us with a better tested (I hope) and better performing reader writer locked dictionary than I could readily muster.

With the NamedLocker class you have explicit control over the scope of the internal locks and the implementation (the parts that matter) are within your explicit control unlike using String.Intern(). I have also added some code to make quick locks a breeze using lambda expressions to specify the scope of the locks.

The only issue with the NamedLocker class is that reading and writing are dealt with in the same way. In the file system cache example I have no problem with multiple readers, I only want to block readers when there is a writer or block the possiblity of multiple concurrent writers. Not having adistinction between readers and writers should be expected to have a slight performance hit for this situation.

  • Pros
    • Provides named locker functionality and is very fast
    • Simple code and easy to understand
  • Cons
    • Handles readers and writers under the same lock which is a little less than ideal for performance
  • Performance


NamedLocker Implementation

The NamedReaderWriterLocker class

Well here you have it: the best performing class I could muster for a situation where readers and writers should be treated differently. It does everything that NamedLocker does but provides different channels of access for readers than writers allowing for very slightly better performance. The downsides are it can be uglier to use and has a bigger memory footprint than any of the other methods. I have also provided some lambda access patterns that abstract away most of the ugliness of using a ReaderWriterLock. Worth it?

  • Pros
    • Provides named locker functionality and is the fastest solution
    • Provides different channels of access for readers and writers reducing contention
  • Cons
    • The code required to implement a ReaderWriterLock is uglier and more error prone than most of the other options
  • Performance


NamedLocker Implementation

Results and Demo Project

Feel free to download the demo project used to generate these results. Each result was calculated from an average of 5 runs on each lock method from a cold start using the constraints in the test project. Hopefully this helped and let us all know if you have found a better way in the comments below!

9 thoughts on “Achieving Named Lock / Locker functionality in C# 4.0

  1. Reply WouterH Apr 25, 2016 7:33 am

    This code is dangerous and could lead to multi-threading issues that are very hard to debug. Consider the scenario where 2 threads are calling NamedReaderWriterLocker.GetLock at the same time and with the same name, the valueFactory s => new ReaderWriterLockSlim() will be executed twice and those 2 threads eventually get a different instance of ReaderWriterLockSlim allowing both threads to execute the same code block.

  2. Reply Zoltán Lehóczky Feb 9, 2015 11:41 am

    There is a slight issue with the last one that ReaderWriterLockSlim is IDisposable. Thus you should make NamedReaderWriterLock implement IDisposable and dispose all the ReaderWriterLockSlim objects.

  3. Reply Bogdan Mart Dec 3, 2014 4:11 pm

    It’s unbelivable, but most time is spent in method GetInteractionType() due to new Random instanc creation. If place it in static field, whol process speeds up.
    Will test further!

  4. Reply Mindaugas Jun 3, 2014 7:16 am

    Hi John,

    Thanks for the samples.

    However I ran into one issue with NamedLocker. You’re using delegates for a new object (s => new object()), which cause the ConcurrentDictionary not to be thread safe anymore (link). But if you change the ‘s => new object()’ with ‘new object()’ it works as expected.

  5. Reply Demirag Apr 26, 2014 7:44 pm

    Hey John,

    Thanks for the NamedReaderWriterLocker, it is working great in my project.

    What would be great is to create an attribute for RunWithWriteLock and RunWithReadLock, so we could use it as follows;

    void Write(string key, Stream s)
    //anycode here

    Stream Read(string key)


  6. Reply zihotki Mar 20, 2014 7:14 pm

    I wanna warn the users of that code that two last lockers are not thread safe and may lead to incorrect behavior if you use RemoveLock methods. That’s because lock (_lockDict.GetOrAdd(name, s => new object())) will be translated to something similar to:

    1: try{
    2: var obj = _lockDict.GetOrAdd(name, s => new object());
    3: Monitor.Enter(obj);

    And there is no critical section between lines 2 and 3, it could happen that between those method calls some other thread could easily call RemoveLock and remove lock object. And all next calls with the same lock name will use different lock object.

    And similar issue applies to the last one.

  7. Reply Dariel Jan 31, 2014 3:52 pm

    Awesome! No nuget package for it?

  8. Reply Travis Jun 24, 2013 7:33 pm

    I tried writing an INamedLocker that uses concurrentdictionary but that does not have memory pressure problems from the dictionary growing forever. thought I was there.

    … but ran in to some troubles with AddOrUpdate not being atomic. Here is the code (+description of the problem)

    Anyways, take a look John if that is interesting to you. Let me know if you have some good ideas :)

Leave a Reply