ReaderWriterLockSlim Class

 

Represents a lock that is used to manage access to a resource, allowing multiple threads for reading or exclusive access for writing.

Namespace:   System.Threading
Assembly:  System.Core (in System.Core.dll)

System.Object
  System.Threading.ReaderWriterLockSlim

<HostProtectionAttribute(SecurityAction.LinkDemand, Synchronization := True,
	ExternalThreading := True)>
<HostProtectionAttribute(SecurityAction.LinkDemand, MayLeakOnAbort := True)>
Public Class ReaderWriterLockSlim
	Implements IDisposable

NameDescription
System_CAPS_pubmethodReaderWriterLockSlim()

Initializes a new instance of the ReaderWriterLockSlim class with default property values.

System_CAPS_pubmethodReaderWriterLockSlim(LockRecursionPolicy)

Initializes a new instance of the ReaderWriterLockSlim class, specifying the lock recursion policy.

NameDescription
System_CAPS_pubpropertyCurrentReadCount

Gets the total number of unique threads that have entered the lock in read mode.

System_CAPS_pubpropertyIsReadLockHeld

Gets a value that indicates whether the current thread has entered the lock in read mode.

System_CAPS_pubpropertyIsUpgradeableReadLockHeld

Gets a value that indicates whether the current thread has entered the lock in upgradeable mode.

System_CAPS_pubpropertyIsWriteLockHeld

Gets a value that indicates whether the current thread has entered the lock in write mode.

System_CAPS_pubpropertyRecursionPolicy

Gets a value that indicates the recursion policy for the current ReaderWriterLockSlim object.

System_CAPS_pubpropertyRecursiveReadCount

Gets the number of times the current thread has entered the lock in read mode, as an indication of recursion.

System_CAPS_pubpropertyRecursiveUpgradeCount

Gets the number of times the current thread has entered the lock in upgradeable mode, as an indication of recursion.

System_CAPS_pubpropertyRecursiveWriteCount

Gets the number of times the current thread has entered the lock in write mode, as an indication of recursion.

System_CAPS_pubpropertyWaitingReadCount

Gets the total number of threads that are waiting to enter the lock in read mode.

System_CAPS_pubpropertyWaitingUpgradeCount

Gets the total number of threads that are waiting to enter the lock in upgradeable mode.

System_CAPS_pubpropertyWaitingWriteCount

Gets the total number of threads that are waiting to enter the lock in write mode.

NameDescription
System_CAPS_pubmethodDispose()

Releases all resources used by the current instance of the ReaderWriterLockSlim class.

System_CAPS_pubmethodEnterReadLock()

Tries to enter the lock in read mode.

System_CAPS_pubmethodEnterUpgradeableReadLock()

Tries to enter the lock in upgradeable mode.

System_CAPS_pubmethodEnterWriteLock()

Tries to enter the lock in write mode.

System_CAPS_pubmethodEquals(Object)

Determines whether the specified object is equal to the current object.(Inherited from Object.)

System_CAPS_pubmethodExitReadLock()

Reduces the recursion count for read mode, and exits read mode if the resulting count is 0 (zero).

System_CAPS_pubmethodExitUpgradeableReadLock()

Reduces the recursion count for upgradeable mode, and exits upgradeable mode if the resulting count is 0 (zero).

System_CAPS_pubmethodExitWriteLock()

Reduces the recursion count for write mode, and exits write mode if the resulting count is 0 (zero).

System_CAPS_protmethodFinalize()

Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.(Inherited from Object.)

System_CAPS_pubmethodGetHashCode()

Serves as the default hash function. (Inherited from Object.)

System_CAPS_pubmethodGetType()

Gets the Type of the current instance.(Inherited from Object.)

System_CAPS_protmethodMemberwiseClone()

Creates a shallow copy of the current Object.(Inherited from Object.)

System_CAPS_pubmethodToString()

Returns a string that represents the current object.(Inherited from Object.)

System_CAPS_pubmethodTryEnterReadLock(Int32)

Tries to enter the lock in read mode, with an optional integer time-out.

System_CAPS_pubmethodTryEnterReadLock(TimeSpan)

Tries to enter the lock in read mode, with an optional time-out.

System_CAPS_pubmethodTryEnterUpgradeableReadLock(Int32)

Tries to enter the lock in upgradeable mode, with an optional time-out.

System_CAPS_pubmethodTryEnterUpgradeableReadLock(TimeSpan)

Tries to enter the lock in upgradeable mode, with an optional time-out.

System_CAPS_pubmethodTryEnterWriteLock(Int32)

Tries to enter the lock in write mode, with an optional time-out.

System_CAPS_pubmethodTryEnterWriteLock(TimeSpan)

Tries to enter the lock in write mode, with an optional time-out.

Use ReaderWriterLockSlim to protect a resource that is read by multiple threads and written to by one thread at a time. ReaderWriterLockSlim allows multiple threads to be in read mode, allows one thread to be in write mode with exclusive ownership of the lock, and allows one thread that has read access to be in upgradeable read mode, from which the thread can upgrade to write mode without having to relinquish its read access to the resource.

System_CAPS_noteNote

ReaderWriterLockSlim is similar to ReaderWriterLock, but it has simplified rules for recursion and for upgrading and downgrading lock state. ReaderWriterLockSlimavoids many cases of potential deadlock. In addition, the performance of ReaderWriterLockSlim is significantly better than ReaderWriterLock. ReaderWriterLockSlim is recommended for all new development.

By default, new instances of ReaderWriterLockSlim are created with the LockRecursionPolicy.NoRecursion flag and do not allow recursion. This default policy is recommended for all new development, because recursion introduces unnecessary complications and makes your code more prone to deadlocks. To simplify migration from existing projects that use Monitor or ReaderWriterLock, you can use the LockRecursionPolicy.SupportsRecursion flag to create instances of ReaderWriterLockSlim that allow recursion.

A thread can enter the lock in three modes: read mode, write mode, and upgradeable read mode. (In the rest of this topic, "upgradeable read mode" is referred to as "upgradeable mode", and the phrase "enter x mode" is used in preference to the longer phrase "enter the lock in x mode".)

Regardless of recursion policy, only one thread can be in write mode at any time. When a thread is in write mode, no other thread can enter the lock in any mode. Only one thread can be in upgradeable mode at any time. Any number of threads can be in read mode, and there can be one thread in upgradeable mode while other threads are in read mode.

System_CAPS_importantImportant

This type implements the IDisposable interface. When you have finished using the type, you should dispose of it either directly or indirectly. To dispose of the type directly, call its Dispose method in a try/catch block. To dispose of it indirectly, use a language construct such as using (in C#) or Using (in Visual Basic). For more information, see the “Using an Object that Implements IDisposable” section in the IDisposable interface topic.

ReaderWriterLockSlim has managed thread affinity; that is, each Thread object must make its own method calls to enter and exit lock modes. No thread can change the mode of another thread.

If a ReaderWriterLockSlim does not allow recursion, a thread that tries to enter the lock can block for several reasons:

  • A thread that tries to enter read mode blocks if there are threads waiting to enter write mode or if there is a single thread in write mode.

    System_CAPS_noteNote

    Blocking new readers when writers are queued is a lock fairness policy that favors writers. The current fairness policy balances fairness to readers and writers, to promote throughput in the most common scenarios. Future versions of the .NET Framework may introduce new fairness policies.

  • A thread that tries to enter upgradeable mode blocks if there is already a thread in upgradeable mode, if there are threads waiting to enter write mode, or if there is a single thread in write mode.

  • A thread that tries to enter write mode blocks if there is a thread in any of the three modes.

Upgradeable mode is intended for cases where a thread usually reads from the protected resource, but might need to write to it if some condition is met. A thread that has entered a ReaderWriterLockSlim in upgradeable mode has read access to the protected resource, and can upgrade to write mode by calling the EnterWriteLock or TryEnterWriteLock methods. Because there can be only one thread in upgradeable mode at a time, upgrading to write mode cannot deadlock when recursion is not allowed, which is the default policy.

System_CAPS_importantImportant

Regardless of recursion policy, a thread that initially entered read mode is not allowed to upgrade to upgradeable mode or write mode, because that pattern creates a strong probability of deadlocks. For example, if two threads in read mode both try to enter write mode, they will deadlock. Upgradeable mode is designed to avoid such deadlocks.

If there are other threads in read mode, the thread that is upgrading blocks. While the thread is blocked, other threads that try to enter read mode are blocked. When all threads have exited from read mode, the blocked upgradeable thread enters write mode. If there are other threads waiting to enter write mode, they remain blocked, because the single thread that is in upgradeable mode prevents them from gaining exclusive access to the resource.

When the thread in upgradeable mode exits write mode, other threads that are waiting to enter read mode can do so, unless there are threads waiting to enter write mode. The thread in upgradeable mode can upgrade and downgrade indefinitely, as long as it is the only thread that writes to the protected resource.

System_CAPS_importantImportant

If you allow multiple threads to enter write mode or upgradeable mode, you must not allow one thread to monopolize upgradeable mode. Otherwise, threads that try to enter write mode directly will be blocked indefinitely, and while they are blocked, other threads will be unable to enter read mode.

A thread in upgradeable mode can downgrade to read mode by first calling the EnterReadLock method and then calling the ExitUpgradeableReadLock method. This downgrade pattern is allowed for all lock recursion policies, even NoRecursion.

After downgrading to read mode, a thread cannot reenter upgradeable mode until it has exited from read mode.

You can create a ReaderWriterLockSlim that supports recursive lock entry by using the ReaderWriterLockSlim(LockRecursionPolicy) constructor that specifies lock policy, and specifying LockRecursionPolicy.SupportsRecursion.

System_CAPS_noteNote

The use of recursion is not recommended for new development, because it introduces unnecessary complications and makes your code more prone to deadlocks.

For a ReaderWriterLockSlim that allows recursion, the following can be said about the modes a thread can enter:

  • A thread in read mode can enter read mode recursively, but cannot enter write mode or upgradeable mode. If it tries to do this, a LockRecursionException is thrown. Entering read mode and then entering write mode or upgradeable mode is a pattern with a strong probability of deadlocks, so it is not allowed. As discussed earlier, upgradeable mode is provided for cases where it is necessary to upgrade a lock.

  • A thread in upgradeable mode can enter write mode and/or read mode, and can enter any of the three modes recursively. However, an attempt to enter write mode blocks if there are other threads in read mode.

  • A thread in write mode can enter read mode and/or upgradeable mode, and can enter any of the three modes recursively.

  • A thread that has not entered the lock can enter any mode. This attempt can block for the same reasons as an attempt to enter a non-recursive lock.

A thread can exit the modes it has entered in any order, as long as it exits each mode exactly as many times as it entered that mode. If a thread tries to exit a mode too many times, or to exit a mode it has not entered, a SynchronizationLockException is thrown.

You may find it useful to think of the lock in terms of its states. A ReaderWriterLockSlim can be in one of four states: not entered, read, upgrade, and write.

  • Not entered: In this state, no threads have entered the lock (or all threads have exited the lock).

  • Read: In this state, one or more threads have entered the lock for read access to the protected resource.

    System_CAPS_noteNote

    A thread can enter the lock in read mode by using the EnterReadLock or TryEnterReadLock methods, or by downgrading from upgradeable mode.

  • Upgrade: In this state, one thread has entered the lock for read access with the option to upgrade to write access (that is, in upgradeable mode), and zero or more threads have entered the lock for read access. No more than one thread at a time can enter the lock with the option to upgrade; additional threads that try to enter upgradeable mode are blocked.

  • Write: In this state, one thread has entered the lock for write access to the protected resource. That thread has exclusive possession of the lock. Any other thread that tries to enter the lock for any reason is blocked.

The following table describes the transitions between lock states, for locks that do not allow recursion, when a thread t takes the action described in the leftmost column. At the time it takes the action, t has no mode. (The special case where t is in upgradeable mode is described in the table footnotes.) The top row describes the starting state of the lock. The cells describe what happens to the thread, and show changes to the lock state in parentheses.

Not entered (N)

Read (R)

Upgrade (U)

Write (W)

t enters read mode

t enters (R).

t blocks if threads are waiting for write mode; otherwise, t enters.

t blocks if threads are waiting for write mode; otherwise, t enters.1

t blocks.

t enters upgradeable mode

t enters (U).

t blocks if threads are waiting for write mode or upgrade mode; otherwise, t enters (U).

t blocks.

t blocks.

t enters write mode

t enters (W).

t blocks.

t blocks.2

t blocks.

1 If t starts out in upgradeable mode, it enters read mode. This action never blocks. The lock state does not change. (The thread can then complete a downgrade to read mode by exiting upgradeable mode.)

2 If t starts out in upgradeable mode, it blocks if there are threads in read mode. Otherwise it upgrades to write mode. The lock state changes to Write (W). If t blocks because there are threads in read mode, it enters write mode as soon as the last thread exits read mode, even if there are threads waiting to enter write mode.

When a state change occurs because a thread exits the lock, the next thread to be awakened is selected as follows:

  • First, a thread that is waiting for write mode and is already in upgradeable mode (there can be at most one such thread).

  • Failing that, a thread that is waiting for write mode.

  • Failing that, a thread that is waiting for upgradeable mode.

  • Failing that, all threads that are waiting for read mode.

The subsequent state of the lock is always Write (W) in the first two cases and Upgrade (U) in the third case, regardless of the state of the lock when the exiting thread triggered the state change. In the last case, the state of the lock is Upgrade (U) if there is a thread in upgradeable mode after the state change, and Read (R) otherwise, regardless of the prior state.

The following example shows a simple synchronized cache that holds strings with integer keys. An instance of ReaderWriterLockSlim is used to synchronize access to the Dictionary(Of TKey, TValue) that serves as the inner cache.

The example includes simple methods to add to the cache, delete from the cache, and read from the cache. To demonstrate time-outs, the example includes a method that adds to the cache only if it can do so within a specified time-out.

To demonstrate upgradeable mode, the example includes a method that retrieves the value associated with a key and compares it with a new value. If the value is unchanged, the method returns a status indicating no change. It no value is found for the key, the key/value pair is inserted. If the value has changed, it is updated. Upgradeable mode allows the thread to upgrade from read access to write access as needed, without the risk of deadlocks.

The example includes a nested enumeration that specifies the return values for the method that demonstrates upgradeable mode.

The example uses the default constructor to create the lock, so recursion is not allowed. Programming the ReaderWriterLockSlim is simpler and less prone to error when the lock does not allow recursion.

Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
Public Class SynchronizedCache
    Private cacheLock As New ReaderWriterLockSlim()
    Private innerCache As New Dictionary(Of Integer, String)

    Public ReadOnly Property Count As Integer
       Get
          Return innerCache.Count
       End Get
    End Property

    Public Function Read(ByVal key As Integer) As String
        cacheLock.EnterReadLock()
        Try
            Return innerCache(key)
        Finally
            cacheLock.ExitReadLock()
        End Try
    End Function

    Public Sub Add(ByVal key As Integer, ByVal value As String)
        cacheLock.EnterWriteLock()
        Try
            innerCache.Add(key, value)
        Finally
            cacheLock.ExitWriteLock()
        End Try
    End Sub

    Public Function AddWithTimeout(ByVal key As Integer, ByVal value As String, _
                                   ByVal timeout As Integer) As Boolean
        If cacheLock.TryEnterWriteLock(timeout) Then
            Try
                innerCache.Add(key, value)
            Finally
                cacheLock.ExitWriteLock()
            End Try
            Return True
        Else
            Return False
        End If
    End Function

    Public Function AddOrUpdate(ByVal key As Integer, _
                                ByVal value As String) As AddOrUpdateStatus
        cacheLock.EnterUpgradeableReadLock()
        Try
            Dim result As String = Nothing
            If innerCache.TryGetValue(key, result) Then
                If result = value Then
                    Return AddOrUpdateStatus.Unchanged
                Else
                    cacheLock.EnterWriteLock()
                    Try
                        innerCache.Item(key) = value
                    Finally
                        cacheLock.ExitWriteLock()
                    End Try
                    Return AddOrUpdateStatus.Updated
                End If
            Else
                cacheLock.EnterWriteLock()
                Try
                    innerCache.Add(key, value)
                Finally
                    cacheLock.ExitWriteLock()
                End Try
                Return AddOrUpdateStatus.Added
            End If
        Finally
            cacheLock.ExitUpgradeableReadLock()
        End Try
    End Function

    Public Sub Delete(ByVal key As Integer)
        cacheLock.EnterWriteLock()
        Try
            innerCache.Remove(key)
        Finally
            cacheLock.ExitWriteLock()
        End Try
    End Sub

    Public Enum AddOrUpdateStatus
        Added
        Updated
        Unchanged
    End Enum

    Protected Overrides Sub Finalize()
       If cacheLock IsNot Nothing Then cacheLock.Dispose()
    End Sub
End Class

The following code then uses the SynchronizedCache object to store a dictionary of vegetable names. It creates three tasks. The first writes the names of vegetables stored in an array to a SynchronizedCache instance. The second and third task display the names of the vegetables, the first in ascending order (from low index to high index), the second in descending order. The final task searches for the string "cucumber" and, when it finds it, calls the EnterUpgradeableReadLock method to substitute the string "green bean".

Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
Public Module Example
   Public Sub Main()
      Dim sc As New SynchronizedCache()
      Dim tasks As New List(Of Task)
      Dim itemsWritten As Integer

      ' Execute a writer.
      tasks.Add(Task.Run( Sub()
                             Dim vegetables() As String = { "broccoli", "cauliflower",
                                                            "carrot", "sorrel", "baby turnip",
                                                            "beet", "brussel sprout",
                                                            "cabbage", "plantain",
                                                            "spinach", "grape leaves",
                                                            "lime leaves", "corn",
                                                            "radish", "cucumber",
                                                            "raddichio", "lima beans" }
                             For ctr As Integer = 1 to vegetables.Length
                                sc.Add(ctr, vegetables(ctr - 1))
                             Next
                             itemsWritten = vegetables.Length
                             Console.WriteLine("Task {0} wrote {1} items{2}",
                                               Task.CurrentId, itemsWritten, vbCrLf)
                          End Sub))
      ' Execute two readers, one to read from first to last and the second from last to first.
      For ctr As Integer = 0 To 1
         Dim flag As Integer = ctr
         tasks.Add(Task.Run( Sub()
                                Dim start, last, stp As Integer
                                Dim items As Integer
                                Do
                                   Dim output As String = String.Empty
                                   items = sc.Count
                                   If flag = 0 Then
                                      start = 1 : stp = 1 : last = items
                                   Else
                                      start = items : stp = -1 : last = 1
                                   End If
                                   For index As Integer = start To last Step stp
                                      output += String.Format("[{0}] ", sc.Read(index))
                                   Next
                                   Console.WriteLine("Task {0} read {1} items: {2}{3}",
                                                           Task.CurrentId, items, output,
                                                           vbCrLf)
                                Loop While items < itemsWritten Or itemsWritten = 0
                             End Sub))
      Next
      ' Execute a red/update task.
      tasks.Add(Task.Run( Sub()
                             For ctr As Integer = 1 To sc.Count
                                Dim value As String = sc.Read(ctr)
                                If value = "cucumber" Then
                                   If sc.AddOrUpdate(ctr, "green bean") <> SynchronizedCache.AddOrUpdateStatus.Unchanged Then
                                      Console.WriteLine("Changed 'cucumber' to 'green bean'")
                                   End If
                                End If
                             Next
                          End Sub ))

      ' Wait for all three tasks to complete.
      Task.WaitAll(tasks.ToArray())

      ' Display the final contents of the cache.
      Console.WriteLine()
      Console.WriteLine("Values in synchronized cache: ")
      For ctr As Integer = 1 To sc.Count
         Console.WriteLine("   {0}: {1}", ctr, sc.Read(ctr))
      Next
   End Sub
End Module
' The example displays output like the following:
'    Task 1 read 0 items:
'
'    Task 3 wrote 17 items
'
'    Task 1 read 17 items: [broccoli] [cauliflower] [carrot] [sorrel] [baby turnip] [
'    beet] [brussel sprout] [cabbage] [plantain] [spinach] [grape leaves] [lime leave
'    s] [corn] [radish] [cucumber] [raddichio] [lima beans]
'
'    Task 2 read 0 items:
'
'    Task 2 read 17 items: [lima beans] [raddichio] [cucumber] [radish] [corn] [lime
'    leaves] [grape leaves] [spinach] [plantain] [cabbage] [brussel sprout] [beet] [b
'    aby turnip] [sorrel] [carrot] [cauliflower] [broccoli]
'
'    Changed 'cucumber' to 'green bean'
'
'    Values in synchronized cache:
'       1: broccoli
'       2: cauliflower
'       3: carrot
'       4: sorrel
'       5: baby turnip
'       6: beet
'       7: brussel sprout
'       8: cabbage
'       9: plantain
'       10: spinach
'       11: grape leaves
'       12: lime leaves
'       13: corn
'       14: radish
'       15: green bean
'       16: raddichio
'       17: lima beans

Universal Windows Platform
Available since 8
.NET Framework
Available since 3.5
Portable Class Library
Supported in: portable .NET platforms
Windows Phone Silverlight
Available since 8.0
Windows Phone
Available since 8.1

This type is thread safe.

Return to top
Show: