Question

I've been using the ReaderWriterLockSlim for some time and it has met my needs up to this point. As I continue to fine-tune my application, I find that ReaderWriterLockSlim is slightly suboptimal for my use case.

Per documentation (and my experience), it favors writers over readers (i.e. when readers and writers are queued up, writers will get preference). However, I need an equivalent that favors readers. I understand the side effects of such component (notably, the writer starvation problem).

Are there any production-ready equivalents that someone can point to? Thanks.

Was it helpful?

Solution

According to MSDN, ReaderWriterLockSlim favors writers. This means that when there are readers and writers in queue, writers will get preference.

This can produce reader starvation, test code to reproduce this is here. I assume that starvation can happen only if writing is a long operation, involving thread context switch. At least it is always reproduced on my machine, so please tell me if I am wrong.

On the other hand, ReaderWriterLock from .net 2.0 does not produce neither reader nor writer starvation, at the cost of reduced performance. Here is modified code from previous sample, to show that no starvation is happening.

So, returning to your question - it depends on what features you require from RW lock. Recursive locks, exception handling, timeouts - closest match to production quality RW lock which supports all of above, and favors readers would probably be ReaderWriterLock.

Also you can adopt code from wiki article describing first readers-writers problem, but of course you would need to implement all required features from above manually, and implementation will have writer-starvation problem.

Lock core can probably look like this:

class AutoDispose : IDisposable 
{ 
  Action _action; 
  public AutoDispose(Action action) 
  { 
    _action = action; 
  }
  public void Dispose()
  {
    _action();
  }
}

class Lock
{
  SemaphoreSlim wrt = new SemaphoreSlim(1);
  int readcount=0;

  public IDisposable WriteLock()
  {
    wrt.Wait();
    return new AutoDispose(() => wrt.Release());
  }

  public IDisposable ReadLock()
  {
    if (Interlocked.Increment(ref readcount) == 1)
        wrt.Wait();

    return new AutoDispose(() => 
    {
      if (Interlocked.Decrement(ref readcount) == 0)
         wrt.Release();
    });
  }
}

Comparing performance of 3 implementations, using 3 reader and 3 writer threads, using simple in-memory operations (using long blocking operation would produce reader-starvation for RWLockSlim and writer-starvation for custom lock):

Performance comparison

I made sure that workload loop is not unrolled by compiler, but there could be other pitfalls I am not aware of, so take these measurements with a grain of salt. Source code for tests is here.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top