ReaderWriterLock
lock
concurrency
multithreading
C#

ReaderWriterLock vs lock

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In C#, lock (which uses Monitor internally) provides exclusive access — only one thread can enter the critical section at a time, whether reading or writing. ReaderWriterLockSlim allows multiple concurrent readers but only one writer, and writers have exclusive access (no readers while writing). The choice depends on your read-to-write ratio: lock is simpler and faster for balanced or write-heavy workloads, while ReaderWriterLockSlim provides better throughput when reads vastly outnumber writes. The older ReaderWriterLock class should not be used — always use ReaderWriterLockSlim.

Using lock (Monitor)

csharp
1public class ThreadSafeCache
2{
3    private readonly object _lock = new object();
4    private readonly Dictionary<string, string> _cache = new();
5
6    public string Get(string key)
7    {
8        lock (_lock)
9        {
10            return _cache.TryGetValue(key, out var value) ? value : null;
11        }
12    }
13
14    public void Set(string key, string value)
15    {
16        lock (_lock)
17        {
18            _cache[key] = value;
19        }
20    }
21
22    public void Remove(string key)
23    {
24        lock (_lock)
25        {
26            _cache.Remove(key);
27        }
28    }
29}

lock is syntactic sugar for Monitor.Enter/Exit. It serializes all access — even concurrent reads block each other. This is correct and simple but limits throughput when many threads read simultaneously.

Using ReaderWriterLockSlim

csharp
1public class RWCache
2{
3    private readonly ReaderWriterLockSlim _rwLock = new();
4    private readonly Dictionary<string, string> _cache = new();
5
6    public string Get(string key)
7    {
8        _rwLock.EnterReadLock();
9        try
10        {
11            return _cache.TryGetValue(key, out var value) ? value : null;
12        }
13        finally
14        {
15            _rwLock.ExitReadLock();
16        }
17    }
18
19    public void Set(string key, string value)
20    {
21        _rwLock.EnterWriteLock();
22        try
23        {
24            _cache[key] = value;
25        }
26        finally
27        {
28            _rwLock.ExitWriteLock();
29        }
30    }
31
32    public IReadOnlyList<string> GetAllKeys()
33    {
34        _rwLock.EnterReadLock();
35        try
36        {
37            return _cache.Keys.ToList();
38        }
39        finally
40        {
41            _rwLock.ExitReadLock();
42        }
43    }
44}

Multiple threads can hold the read lock simultaneously. When a thread enters the write lock, it waits for all readers to exit and blocks new readers until the write completes.

Upgradeable Read Lock

csharp
1public class UpgradeableCache
2{
3    private readonly ReaderWriterLockSlim _rwLock = new();
4    private readonly Dictionary<string, int> _cache = new();
5
6    public int GetOrCompute(string key, Func<int> computeValue)
7    {
8        // Start with an upgradeable read lock
9        _rwLock.EnterUpgradeableReadLock();
10        try
11        {
12            if (_cache.TryGetValue(key, out var value))
13            {
14                return value;  // cache hit — read only
15            }
16
17            // Cache miss — upgrade to write lock
18            _rwLock.EnterWriteLock();
19            try
20            {
21                // Double-check after acquiring write lock
22                if (_cache.TryGetValue(key, out value))
23                    return value;
24
25                value = computeValue();
26                _cache[key] = value;
27                return value;
28            }
29            finally
30            {
31                _rwLock.ExitWriteLock();
32            }
33        }
34        finally
35        {
36            _rwLock.ExitUpgradeableReadLock();
37        }
38    }
39}

EnterUpgradeableReadLock allows one thread to read and conditionally upgrade to a write lock without releasing the read lock first. Only one upgradeable lock can be held at a time (to prevent deadlocks), but other readers can proceed concurrently.

Performance Comparison

csharp
1// Benchmark scenario: 90% reads, 10% writes, 8 threads
2// Results vary by workload — always benchmark your specific case
3
4// lock:
5//   - All threads serialized
6//   - Low overhead per acquisition (~20ns)
7//   - Throughput limited by serialization
8
9// ReaderWriterLockSlim:
10//   - Readers run concurrently
11//   - Higher overhead per acquisition (~40ns for read, ~60ns for write)
12//   - Better throughput when reads dominate
13
14// Rule of thumb:
15// - < 80% reads: lock is simpler and often faster
16// - > 90% reads: ReaderWriterLockSlim provides better throughput
17// - Critical sections < 1μs: lock wins (overhead of RW lock dominates)
18// - Critical sections > 10μs: ReaderWriterLockSlim wins
FeaturelockReaderWriterLockSlim
Concurrent readersNoYes
Lock acquisition overhead~20ns~40-60ns
Code complexitySimpleModerate (try/finally)
Upgradeable locksNoYes
Supports recursionYes (reentrant)Optional (LockRecursionPolicy)
Best forWrite-heavy, short critical sectionsRead-heavy, longer critical sections

Using ConcurrentDictionary as an Alternative

csharp
1using System.Collections.Concurrent;
2
3// For simple key-value scenarios, ConcurrentDictionary avoids manual locking
4var cache = new ConcurrentDictionary<string, string>();
5
6// Thread-safe without explicit locks
7cache.TryAdd("key", "value");
8cache.TryGetValue("key", out var result);
9cache.AddOrUpdate("key", "new", (k, old) => "updated");
10cache.GetOrAdd("key", k => ComputeExpensiveValue(k));

For simple cache scenarios, ConcurrentDictionary is often the best choice — it handles synchronization internally using fine-grained locking and is optimized for concurrent access.

Common Pitfalls

  • Using ReaderWriterLock instead of ReaderWriterLockSlim: The older ReaderWriterLock class has significantly worse performance and is prone to deadlocks with recursive locking. Always use ReaderWriterLockSlim, which was introduced in .NET 3.5 as its replacement.
  • Forgetting try/finally with ReaderWriterLockSlim: Unlike lock (which guarantees Monitor.Exit in all cases), ReaderWriterLockSlim requires explicit Exit calls. If an exception occurs between Enter and Exit, the lock is never released, causing all subsequent threads to hang. Always use try/finally.
  • Using ReaderWriterLockSlim for short critical sections: If the code inside the lock runs in microseconds, the overhead of acquiring a reader-writer lock (~40ns) outweighs the benefit of concurrent reads. A simple lock (~20ns) performs better for very short critical sections.
  • Enabling recursion without need: new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion) adds significant overhead. The default NoRecursion policy is faster and prevents accidental recursive locking bugs. Only enable recursion if your code genuinely requires it.
  • Not disposing ReaderWriterLockSlim: ReaderWriterLockSlim implements IDisposable and holds kernel wait handles internally. Failing to dispose it causes resource leaks in long-running applications. Dispose it when the containing object is no longer needed.

Summary

  • lock provides exclusive access with low overhead — best for write-heavy or short critical sections
  • ReaderWriterLockSlim allows concurrent readers — best when reads outnumber writes by 10:1 or more
  • Always use ReaderWriterLockSlim (not the older ReaderWriterLock)
  • Wrap EnterReadLock/EnterWriteLock in try/finally to ensure locks are released
  • Consider ConcurrentDictionary for simple key-value cache scenarios
  • Benchmark your specific workload — the theoretical advantage of reader-writer locks does not always translate to real-world speedups

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.