C# ReaderWriterLockSlim

Summary: in this tutorial, you will learn how to use the C# ReaderWriterLockSlim class to control access to a shared resource.

Introduction to C# ReaderWriterLockSlim class

Sometimes, you have multiple threads that read from a shared resource but only have a few threads that write to it. In this case, using the lock statement may decrease the application’s performance.

The reason is that the lock statement may block multiple threads from reading the shared resource even though these threads don’t need exclusive access.

The ReaderWriterLockSlim class allows multiple threads to read from the shared resource simultaneously, but one thread to write to it at a time. Therefore, the ReaderWriterLockSlim can help improve performance in comparison with the lock statement.

The ReaderWriterLockSlim has two kinds of locks:

  • Read lock
  • Writer lock

The ReaderWriterLockSlim allows multiple threads to acquire the read lock at the same time, as long as no thread acquired the writer lock.

Also, it only allows a single thread to acquire the write lock at a time and blocks other threads from trying to acquire either the read or write lock.

In practice, the ReaderWriterLockSlim is useful for scenarios where your applications have multiple readers and fewer writers.

Note that the ReaderWriterLockSlim incurs some overhead. Therefore, you should use it when the benefits of concurrent access are higher than the cost of acquiring and releasing the lock.

C# ReaderWriterLockSlim example

The following program demonstrates how to use the ReaderWriterLockSlim object to control concurrent access to a shared variable with five readers and two writers:

using static System.Console;

int counter = 0;
ReaderWriterLockSlim _lock = new();

void Read()
{
    while (true)
    {

        try
        {
            _lock.EnterReadLock();
            WriteLine($"R: Thread {Thread.CurrentThread.ManagedThreadId} is reading: {counter}");
        }
        finally
        {
            _lock.ExitReadLock();
        }

        Thread.Sleep(500);
    }
}

void Write()
{
    while (true)
    {
        try
        {
            _lock.EnterWriteLock();
            WriteLine($"W: Thread {Thread.CurrentThread.ManagedThreadId} is writing: {counter++}");
        }
        finally
        {
            _lock.ExitWriteLock();
        }

        Thread.Sleep(2000);
    }
}


// create 5 reader threads
for (int i = 0; i < 5; i++)
{
    new Thread(() => Read()).Start();
}

// create 2 writer threads
for (int i = 0; i < 2; i++)
{
    new Thread(() => Write()).Start();
}Code language: C# (cs)

How it works.

First, declare an integer variable counter:

int counter = 0;Code language: C# (cs)

Second, create a new ReaderWriterLockSlim object called _lock:

ReaderWriterLockSlim _lock = new();Code language: C# (cs)

Third, define the Reader() method that acquires a read lock on the _lock object by calling the EnterReadLock() method in the try block:

void Read()
{
    while (true)
    {

        try
        {
            _lock.EnterReadLock();
            WriteLine($"R: Thread {Thread.CurrentThread.ManagedThreadId} is reading: {counter}");
        }
        finally
        {
            _lock.ExitReadLock();
        }

        Thread.Sleep(500);
    }
}Code language: C# (cs)

The Read() method displays the value of the counter variable.

Also, it releases the read lock on the _lock object in the finally block by calling the ExitReadLock() method.

The Read() method delays for 500 milliseconds, which is a half of second.

Fourth, define the Write method that increases the shared variable counter:

void Write()
{
    while (true)
    {
        try
        {
            _lock.EnterWriteLock();
            WriteLine($"W: Thread {Thread.CurrentThread.ManagedThreadId} is writing: {counter++}");
        }
        finally
        {
            _lock.ExitWriteLock();
        }

        Thread.Sleep(2000);
    }
}Code language: C# (cs)

The Write method acquires a write lock on the _lock object in the try block by calling the EnterWriteLock() method.

After that, it increases the counter by one and displays the counter value to the console. The Write method releases the write lock in the finally block by calling the ExitWriteLock() method.

The Write method has a delay of 2000 milliseconds, which is 2 seconds.

Fifth, create two reader threads and two writer threads. The reader threads execute the Read method while the writer threads execute the Write method.

// create 5 reader threads
for (int i = 0; i < 5; i++)
{
    new Thread(() => Read()).Start();
}

// create 2 writer threads
for (int i = 0; i < 2; i++)
{
    new Thread(() => Write()).Start();
}Code language: C# (cs)

Here’s a sample output of the program:

R: Thread 9 is reading: 0
R: Thread 8 is reading: 0
R: Thread 10 is reading: 0
R: Thread 11 is reading: 0
W: Thread 13 is writing: 0
W: Thread 12 is writing: 1
R: Thread 7 is reading: 2
R: Thread 10 is reading: 2
R: Thread 11 is reading: 2
R: Thread 7 is reading: 2
R: Thread 9 is reading: 2
R: Thread 8 is reading: 2
R: Thread 7 is reading: 2
R: Thread 11 is reading: 2
R: Thread 8 is reading: 2
R: Thread 9 is reading: 2
R: Thread 10 is reading: 2
R: Thread 9 is reading: 2
R: Thread 8 is reading: 2
R: Thread 11 is reading: 2
R: Thread 10 is reading: 2
R: Thread 7 is reading: 2
W: Thread 13 is writing: 2
W: Thread 12 is writing: 3
R: Thread 8 is reading: 4
R: Thread 9 is reading: 4
R: Thread 9 is reading: 4
R: Thread 10 is reading: 4
R: Thread 7 is reading: 4
R: Thread 11 is reading: 4
R: Thread 8 is reading: 4
R: Thread 8 is reading: 4
R: Thread 11 is reading: 4
R: Thread 7 is reading: 4
R: Thread 10 is reading: 4
R: Thread 9 is reading: 4
...Code language: C# (cs)

To terminate the program, you need to close it.

Summary

  • Use C# ReaderWriterLockSlim to allow multiple threads to read from the shared resource simultaneously, but one thread to write to it at a time.
Was this tutorial helpful ?