Threading — Using ReaderWriterLockSlim

When deal­ing with concurrency/threading issues in .Net, the nor­mal approach is to use lock() to lock a ded­i­cat­ed sync object like this:

private static readonly object padlock = new object();
…
lock(padlock)
{
     // enter critical section here
}

This is an effi­cient, sim­ple and well proven way to get thread-safe­ty in .Net and is prob­a­bly all you’ll ever need in your project. How­ev­er, as this approach ensures only one thread can enter the crit­i­cal sec­tion at any one time it’s not ide­al for man­ag­ing access to a shared resource because all read and write oper­a­tions must be made sequen­tial­ly and this results in a per­for­mance hit on your appli­ca­tion. Ide­al­ly all threads should be able to read the shared resource simul­ta­ne­ous­ly but only one thread can update it and no oth­er threads can read from the resource whilst the update is hap­pen­ing.

.Net’s answer to this used to be the Read­er­Writer­Lock, which since .Net 3.5 has been super­seded by the bet­ter, more light­weight and per­form­ing Read­er­Writer­Lock­Slim which Microsoft is rec­om­mend­ing for all new devel­op­ment.

The Read­er­Writer­Lock­Slim has three modes: Read, Ungrade­able and Write:

  • many threads can enter Read lock simul­ta­ne­ous­ly
  • only one thread can enter Ungrade­able lock but oth­er threads can still enter Read lock
  • only one thread can enter Write lock and no oth­er thread can enter any lock

In prac­tice though, almost all thread­ing prob­lems can be solved using read and write locks alone. To enter read/write lock, you need some­thing like this:

private static readonly ReaderWriteLockSlim rwlock = new ReaderWriterLockSlim();
…
rwlock.EnterReadLock()
try
{
     // do something here
}
finally
{
     // don't forget to release the lock afterwards!
     rwlock.ExitReadLock();
}

As you can see, this can seri­ous­ly clut­ter your code if you require read/write locks in many places! I saw this post on Stack­Over­flow quite some time back and real­ly like the pat­tern that Marc has pre­sent­ed:

http://stackoverflow.com/questions/170028/how-would-you-simplfy-entering-and-exiting-a-readerwriterlock

In his answer he showed how you can sim­pli­fy the code need­ed to enter a read lock, here’s the full imple­men­ta­tion you can use in your project:

public static class ReaderWriterLockSlimExtensions
{
     private sealed class ReadLockToken : IDisposable
     {
          private ReaderWriterLockSlim _sync;
          public ReadLockToken(ReaderWriterLockSlim sync)
          {
               _sync = sync;
               sync.EnterReadLock();
          }
          public void Dispose()
          {
               if (_sync != null)
               {
                    _sync.ExitReadLock();
                    _sync = null;
               }
          }
     }
     private sealed class WriteLockToken : IDisposable
     {
          private ReaderWriterLockSlim _sync;
          public WriteLockToken(ReaderWriterLockSlim sync)
          {
               _sync = sync;
               sync.EnterWriteLock();
          }
          public void Dispose()
          {
               if (_sync != null)
               {
                    _sync.ExitWriteLock();
                    _sync = null;
               }
          }
     }

     public static IDisposable Read(this ReaderWriterLockSlim obj)
     {
          return new ReadLockToken(obj);
     }
     public static IDisposable Write(this ReaderWriterLockSlim obj)
     {
          return new WriteLockToken(obj);
     }
}

And to use it in your code, this is all you’ll need:

using (_sync.Read())
{
     // do reading here
}
using (_sync.Write())
{
     // do writing here
}

Further Readings:

Jon Skeet’s tuto­r­i­al on Mul­ti-Thread­ing in .Net

Ayande has a post on using the Upgrade­ableRead­Lock