When Is Readerwriterlockslim Better Than a Simple Lock

When is ReaderWriterLockSlim better than a simple lock?

In your example, the sleeps mean that generally there is no contention. An uncontended lock is very fast. For this to matter, you would need a contended lock; if there are writes in that contention, they should be about the same (lock may even be quicker) - but if it is mostly reads (with a write contention rarely), I would expect the ReaderWriterLockSlim lock to out-perform the lock.

Personally, I prefer another strategy here, using reference-swapping - so reads can always read without ever checking / locking / etc. Writes make their change to a cloned copy, then use Interlocked.CompareExchange to swap the reference (re-applying their change if another thread mutated the reference in the interim).

ReaderWriterLock vs lock{}

lock allows only one thread to execute the code at the same time. ReaderWriterLock may allow multiple threads to read at the same time or have exclusive access for writing, so it might be more efficient. If you are using .NET 3.5 ReaderWriterLockSlim is even faster. So if your shared resource is being read more often than being written, use ReaderWriterLockSlim. A good example for using it is a file that you read very often (on each request) and you update the contents of the file rarely. So when you read from the file you enter a read lock so that many requests can open it for reading and when you decide to write you enter a write lock. Using a lock on the file will basically mean that you can serve one request at a time.

Why lock is 240% faster than ReaderWriterLockSlim?

Thanks to canton7 and Kevin Gosse, I found my 2013 question perfectly answered by Hans Passant: When exactly does .NET Monitor go to kernel-mode?

So lock is faster in a no-contention scenario simply because it has lighter logic and kernel mode is not involved.

ReaderWriterLockSlim with LockRecursionPolicy.SupportsRecursion vs lock

As explained here, the lock keyword in C# is based on a Monitor object, an exclusive synchronization mechanism. "Exclusive" means that, when the first thread enters the critical section, any subsequent threads are blocked.

ReaderWriterLockSlim, on the other hand, distinguishes between reader locks and writer locks. They are intended to be used in (and provide improved concurrency in) scenarios where there are many readers but only occasional write updates. Reader/Writer locks are non-exclusive.

A lock knows which thread it was locked on, so if that thread re-enters the critical section, it just increments a counter and continues.

A ReaderWriterLockSlim is in a more complicated position. Because it distinguishes between read locks and write locks, and because it is sometimes desirable to lock on a write without creating a race condition, the ReaderWriterLockSlim offers an UpgradableLock that allows you to temporarily enhance the lock for write capabilities without worrying about a race condition caused by a rogue write from another thread coming in while you transition to write mode.

As you can see, ReaderWriterLockSlim offers a more feature-rich, but also more complex model for synchronization than lock does. Its requirement to declare your intention to use recursion is an admission of this additional complexity.

Further Reading
Synchronization Essentials: Locking

Advanced Threading: Reader/Writer Locks

Why Lock Recursion is Generally a Bad Idea Anyway

Is ReaderWriterLockSlim the right choice?

OK, it entirely depends on how resource contention is expected. Following is a simple decision I would make based on what I'm locking and how much locking.

ReaderWriterLockSlim is implemented using a spinlock, therefore if you have long locking resources (writing text in this case) it would lead to a worse performance because of spinning by waiting threads. That said, its very useful tool in following situations.

  • If you have a lot of locks and each of them is finer grained (locks for very small pieces of code) then ReaderWriterLockSlim or (spinlock).
  • Number of threads or contentions expected is high spinlock makes sense provided locking is fine grained.

Lock or Monitor is best suited when your contentions are coarsegrained and you know that contentions or number of locks are lower.

ReaderWriterLockSlim is comparatively faster than ReaderWriterLock atleast 3-5 times.

ReaderWriterLockSlim vs Double Lock Check pattern

From MSDN:

Only one thread can enter upgradeable mode at any given time

basically, you haven't done much better than just using a full lock - except lock would actually have been faster.

Oddly enough, one good approach here is Hashtable; especially since the value is object, and the key is a reference-type (no extra boxing). Hashtable is unusual in that reads are fully thread-safe; you only need to guard against multiple writers.

For example:

readonly Hashtable lookup = new Hashtable();

...

object val = lookup[key]; // no need to synchronize when reading

...

lock(lookup)
{
lookup[key] = newVal; // synchronize when writing
}

Why use ReaderWriterLockSlim?

They have different semantics. A Monitor (lock) allows one thread to own the "lock", and that's it. A ReaderWriterLock[Slim] allows any number of readers, or (one writer and nobody else).

So a reader-writer setup might be useful if writes are rare and reads take a non-trivial amount of time, so that you can have multiple concurrent readers, and yet lock everyone out when you need to mutate the data.

Neither is "better". They do different things. They each have scenarios in which they are ideal, and scenarios in which they are poor choices.

Why lock is 240% faster than ReaderWriterLockSlim?

Thanks to canton7 and Kevin Gosse, I found my 2013 question perfectly answered by Hans Passant: When exactly does .NET Monitor go to kernel-mode?

So lock is faster in a no-contention scenario simply because it has lighter logic and kernel mode is not involved.

ReaderWriterLockSlim vs. Monitor

For write-only load the Monitor is cheaper than ReaderWriterLockSlim, however, if you simulate read + write load where read is much greater than write, then ReaderWriterLockSlim should out perform Monitor.

Is ReaderWriterLockSlim.EnterReadLock necessary for ListSomeObject.Count?

If you want to be guaranteed that any write operation has completed before you read the count a read lock is required. Assuming that write locks are used for all write actions.

To enter a write lock inside a read lock you have to use EnterUpgradeableReadLock . Calling EnterWriteLock when in a read lock will throw an exception. If you should use a write lock or upgradable lock please read https://stackoverflow.com/a/26578074/9271844.

For more information about the ReaderWriterLockSlim class refer to https://learn.microsoft.com/en-us/dotnet/api/system.threading.readerwriterlockslim?view=net-5.0



Related Topics



Leave a reply



Submit