Home > Articles > Programming > C#

.NET Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

The ReaderWriterLockSlim Synchronization Primitive

Last updated Mar 14, 2003.

.NET 3.5 introduced a new synchronization primitive called ReaderWriterLockSlim in the System.Threading namespace, which provides synchronized access to a resource for multiple readers, or one writer. Conceptually, ReaderWriterLockSlim does the same thing as ReaderWriterLock. But ReaderWriterLockSlim performs much better and is less prone to deadlocks. The .NET team recommends ReaderWriterLockSlim for all new development.

In addition to better performance and fewer opportunities for deadlock, ReaderWriterLockSlim also has a better public API that is easier to use than that of ReaderWriterLock. The difference isn’t evident from a brief perusal of the class methods, but when you compare comparable code it becomes obvious.

The code below attempts to acquire a reader lock. If it can’t acquire the lock, it does something else for a while and then tries again. This is a reasonably common thing to do: continue processing and check periodically to see if the shared resource is available.

First, using ReaderWriterLock:

ReaderWriterLock rwlock = new ReaderWriterLock();
private void DoSomething()
{
  while (!GetTheLock())
  {
	// Do stuff for a while before trying the lock again.
  }
  // We have the lock. Do what we need to do.
  try
  {
	// do stuff
  }
  finally
  {
	// be sure to release the lock
	rwlock.ExitReadLock();
  }
}

private bool GetTheLock()
{
  try
  {
	rwlock.AcquireReaderLock(0);
	return true;
  }
  catch (ApplicationException)
  {
	return false;
  }
}

The annoyance here is that AcquireReaderLock throws an exception if it fails to acquire the lock within the timeout period. The only approachable way to use AcquireReaderLock in this situation is to create a separate method that acquires the lock. Structuring your code around that exception gets ugly very quickly. This is an inappropriate use of exceptions because a busy resource is not an exceptional condition.

ReaderWriterLockSlim has a friendlier interface--similar to Monitor. Rather than a single AcquireReaderLock method to acquire a reader lock, it has two different methods: EnterReadLock and TryEnterReadLock. EnterReadLock waits indefinitely for the lock, just as Monitor.Enter (and the lock and SyncLock keywords in C# and Visual Basic) will wait indefinitely for the lock. That is analogous to calling ReaderWriterLock.AcquireReaderLock with the value Timeout.Infinite.

TryEnterReadLock attempts to acquire the lock (waiting for the passed timeout period), and then returns a Boolean value telling you whether or not the lock was acquired. This makes it much easier to implement the "do something until the lock is available" pattern:

ReaderWriterLockSlim rwlock = new ReaderWriterLockSlim();
private void DoSomething()
{
  while (!rwlock.TryEnterReadLock(0))
  {
	// Do stuff for a while before trying the lock again.
  }
  // We have the lock. Do what we need to do.
  try
  {
	// do stuff
  }
  finally
  {
	// be sure to release the lock
	rwlock.ExitReadLock();
  }
}

On the surface, the only real difference is that we replaced a call to our GetTheLock wrapper with a call to the object’s TryEnterReadLock method. I still think the code is cleaner because it’s obvious what is happening. And, it avoids the overhead of exception handling.

Acquiring a writer lock and upgrading a reader lock to a writer lock are similarly streamlined with ReaderWriterLockSlim.

The new class also contains additional diagnostics that are not implemented in ReaderWriterLock. Public properties of ReaderWriterLockSlim will tell you how many times the various types of locks have been entered, how many threads are currently reading, or waiting on particular types of locks. All in all, it’s a much better and easier interface to work with.

Beyond the interface, the primary difference that you’ll need to be aware of is that by default ReaderWriterLockSlim does not support recursion, whereas ReaderWriterLock always does. Overall this is a Good Thing, because it reduces complexity and also makes deadlocks less likely to occur.

However, if you’re replacing existing ReaderWriterLock code that you know uses recursive locks, you can create a ReaderWriterLockSlim object instance that supports recursion by passing the LockRecursionPolicy.SupportsRecursion flag to the constructor. I would recommend, however, that you consider restructuring your code so that it doesn’t require recursion.