Export (0) Print
Expand All

ReaderWriterLock Class

Defines a lock that supports single writers and multiple readers.

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

'Declaration
<ComVisibleAttribute(True)> _
<HostProtectionAttribute(SecurityAction.LinkDemand, Synchronization := True,  _
	ExternalThreading := True)> _
Public NotInheritable Class ReaderWriterLock _
	Inherits CriticalFinalizerObject

The ReaderWriterLock type exposes the following members.

  NameDescription
Public methodReaderWriterLockInitializes a new instance of the ReaderWriterLock class.
Top

  NameDescription
Public propertyIsReaderLockHeldGets a value indicating whether the current thread holds a reader lock.
Public propertyIsWriterLockHeldGets a value indicating whether the current thread holds the writer lock.
Public propertyWriterSeqNumGets the current sequence number.
Top

  NameDescription
Public methodAcquireReaderLock(Int32)Acquires a reader lock, using an Int32 value for the time-out.
Public methodAcquireReaderLock(TimeSpan)Acquires a reader lock, using a TimeSpan value for the time-out.
Public methodAcquireWriterLock(Int32)Acquires the writer lock, using an Int32 value for the time-out.
Public methodAcquireWriterLock(TimeSpan)Acquires the writer lock, using a TimeSpan value for the time-out.
Public methodAnyWritersSinceIndicates whether the writer lock has been granted to any thread since the sequence number was obtained.
Public methodDowngradeFromWriterLockRestores the lock status of the thread to what it was before UpgradeToWriterLock was called.
Public methodEquals(Object)Determines whether the specified object is equal to the current object. (Inherited from Object.)
Protected methodFinalizeEnsures that resources are freed and other cleanup operations are performed when the garbage collector reclaims the ReaderWriterLock object. (Overrides CriticalFinalizerObject.Finalize.)
Public methodGetHashCodeServes as the default hash function. (Inherited from Object.)
Public methodGetTypeGets the Type of the current instance. (Inherited from Object.)
Public methodReleaseLockReleases the lock, regardless of the number of times the thread acquired the lock.
Public methodReleaseReaderLockDecrements the lock count.
Public methodReleaseWriterLockDecrements the lock count on the writer lock.
Public methodRestoreLockRestores the lock status of the thread to what it was before calling ReleaseLock.
Public methodToStringReturns a string that represents the current object. (Inherited from Object.)
Public methodUpgradeToWriterLock(Int32)Upgrades a reader lock to the writer lock, using an Int32 value for the time-out.
Public methodUpgradeToWriterLock(TimeSpan)Upgrades a reader lock to the writer lock, using a TimeSpan value for the time-out.
Top

Important noteImportant

The .NET Framework has two reader-writer locks, ReaderWriterLockSlim and ReaderWriterLock. ReaderWriterLockSlim is recommended for all new development. ReaderWriterLockSlim is similar to ReaderWriterLock, but it has simplified rules for recursion and for upgrading and downgrading lock state. ReaderWriterLockSlim avoids many cases of potential deadlock. In addition, the performance of ReaderWriterLockSlim is significantly better than ReaderWriterLock.

ReaderWriterLock is used to synchronize access to a resource. At any given time, it allows either concurrent read access for multiple threads, or write access for a single thread. In a situation where a resource is changed infrequently, a ReaderWriterLock provides better throughput than a simple one-at-a-time lock, such as Monitor.

ReaderWriterLock works best where most accesses are reads, while writes are infrequent and of short duration. Multiple readers alternate with single writers, so that neither readers nor writers are blocked for long periods.

NoteNote

Holding reader locks or writer locks for long periods will starve other threads. For best performance, consider restructuring your application to minimize the duration of writes.

A thread can hold a reader lock or a writer lock, but not both at the same time. Instead of releasing a reader lock in order to acquire the writer lock, you can use UpgradeToWriterLock and DowngradeFromWriterLock.

Recursive lock requests increase the lock count on a lock.

Readers and writers are queued separately. When a thread releases the writer lock, all threads waiting in the reader queue at that instant are granted reader locks; when all of those reader locks have been released, the next thread waiting in the writer queue, if any, is granted the writer lock, and so on. In other words, ReaderWriterLock alternates between a collection of readers, and one writer.

While a thread in the writer queue is waiting for active reader locks to be released, threads requesting new reader locks accumulate in the reader queue. Their requests are not granted, even though they could share concurrent access with existing reader-lock holders; this helps protect writers against indefinite blockage by readers.

Most methods for acquiring locks on a ReaderWriterLock accept time-out values. Use time-outs to avoid deadlocks in your application. For example, a thread might acquire the writer lock on one resource and then request a reader lock on a second resource; in the meantime, another thread might acquire the writer lock on the second resource, and request a reader lock on the first. Unless time-outs are used, the threads deadlock.

If the time-out interval expires and the lock request has not been granted, the method returns control to the calling thread by throwing an ApplicationException. A thread can catch this exception and determine what action to take next.

Time-outs are expressed in milliseconds. If you use a System.TimeSpan to specify the time-out, the value used is the total number of whole milliseconds represented by the TimeSpan. The following table shows the valid time-out values in milliseconds.

Value

Description

-1

The thread waits until the lock is acquired, regardless of how long it takes. For methods that specify integer time-outs, the constant Infinite can be used.

0

The thread does not wait to acquire the lock. If the lock cannot be acquired immediately, the method returns.

>0

The number of milliseconds to wait.

With the exception of -1, negative time-out values are not allowed. If you specify a negative integer other than -1, a time-out value of zero is used instead. (That is, the method returns without waiting, if the lock cannot be acquired immediately.) If you specify a TimeSpan that represents a negative number of milliseconds other than -1, ArgumentOutOfRangeException is thrown.

NoteNote

The HostProtectionAttribute attribute applied to this type or member has the following Resources property value: Synchronization | ExternalThreading. The HostProtectionAttribute does not affect desktop applications (which are typically started by double-clicking an icon, typing a command, or entering a URL in a browser). For more information, see the HostProtectionAttribute class or SQL Server Programming and Host Protection Attributes.

The following example demonstrates how to use a ReaderWriterLock to protect a shared resource, an integer value named resource, that is read concurrently and written exclusively by multiple threads. Note that the ReaderWriterLock is declared at the class level so that it is visible to all threads.

' The complete code is located in the ReaderWriterLock class topic. 
Imports System.Threading

Public Module Example
   Private rwl As New ReaderWriterLock()
   ' Define the shared resource protected by the ReaderWriterLock. 
   Private resource As Integer = 0

   Const numThreads As Integer = 26
   Private running As Boolean = True 
   Private rnd As New Random()

   ' Statistics. 
   Private readerTimeouts As Integer = 0
   Private writerTimeouts As Integer = 0
   Private reads As Integer = 0
   Private writes As Integer = 0

   Public Sub Main()
      ' Start a series of threads to randomly read from and 
      ' write to the shared resource. 
      Dim t(numThreads - 1) As Thread
      Dim i As Integer 
      For i = 0 To numThreads - 1
         t(i) = New Thread(New ThreadStart(AddressOf ThreadProc))
         t(i).Name = Chr(i + 65)
         t(i).Start()
         If i > 10 Then
            Thread.Sleep(300)
         End If 
      Next 

      ' Tell the threads to shut down and wait until they all finish.
      running = False 
      For i = 0 To numThreads - 1
         t(i).Join()
      Next 

      ' Display statistics.
      Console.WriteLine(vbCrLf & "{0} reads, {1} writes, {2} reader time-outs, {3} writer time-outs.",
                        reads, writes, readerTimeouts, writerTimeouts)
      Console.Write("Press ENTER to exit... ")
      Console.ReadLine()
   End Sub 

   Sub ThreadProc()
      ' Randomly select a way for the thread to read and write from the shared 
      ' resource. 
      While running
         Dim action As Double = rnd.NextDouble()
         If action < 0.8 Then
            ReadFromResource(10)
         ElseIf action < 0.81 Then
            ReleaseRestore(50)
         ElseIf action < 0.9 Then
            UpgradeDowngrade(100)
         Else
            WriteToResource(100)
         End If 
      End While 
   End Sub 

   ' Request and release a reader lock, and handle time-outs. 
   Sub ReadFromResource(timeOut As Integer)
      Try
         rwl.AcquireReaderLock(timeOut)
         Try 
            ' It's safe for this thread to read from the shared resource.
            Display("reads resource value " & resource)
            Interlocked.Increment(reads)
         Finally 
            ' Ensure that the lock is released.
            rwl.ReleaseReaderLock()
         End Try 
      Catch ex As ApplicationException
         ' The reader lock request timed out.
         Interlocked.Increment(readerTimeouts)
      End Try 
   End Sub 

   ' Request and release the writer lock, and handle time-outs. 
   Sub WriteToResource(timeOut As Integer)
      Try
         rwl.AcquireWriterLock(timeOut)
         Try 
            ' It's safe for this thread to read or write from the shared resource.
            resource = rnd.Next(500)
            Display("writes resource value " & resource)
            Interlocked.Increment(writes)
         Finally 
            ' Ensure that the lock is released.
            rwl.ReleaseWriterLock()
         End Try 
      Catch ex As ApplicationException
         ' The writer lock request timed out.
         Interlocked.Increment(writerTimeouts)
      End Try 
   End Sub 

   ' Requests a reader lock, upgrades the reader lock to the writer 
   ' lock, and downgrades it to a reader lock again. 
   Sub UpgradeDowngrade(timeOut As Integer)
      Try
         rwl.AcquireReaderLock(timeOut)
         Try 
            ' It's safe for this thread to read from the shared resource.
            Display("reads resource value " & resource)
            Interlocked.Increment(reads)

            ' To write to the resource, either release the reader lock and 
            ' request the writer lock, or upgrade the reader lock. Upgrading 
            ' the reader lock puts the thread in the write queue, behind any 
            ' other threads that might be waiting for the writer lock. 
            Try 
               Dim lc As LockCookie = rwl.UpgradeToWriterLock(timeOut)
               Try 
                  ' It's safe for this thread to read or write from the shared resource.
                  resource = rnd.Next(500)
                  Display("writes resource value " & resource)
                  Interlocked.Increment(writes)
               Finally 
                  ' Ensure that the lock is released.
                  rwl.DowngradeFromWriterLock(lc)
               End Try 
            Catch ex As ApplicationException
               ' The upgrade request timed out.
               Interlocked.Increment(writerTimeouts)
            End Try 

            ' If the lock was downgraded, it's still safe to read from the resource.
            Display("reads resource value " & resource)
            Interlocked.Increment(reads)
         Finally 
            ' Ensure that the lock is released.
            rwl.ReleaseReaderLock()
         End Try 
      Catch ex As ApplicationException
         ' The reader lock request timed out.
         Interlocked.Increment(readerTimeouts)
      End Try 
   End Sub 

   ' Release all locks and later restores the lock state. 
   ' Uses sequence numbers to determine whether another thread has 
   ' obtained a writer lock since this thread last accessed the resource. 
   Sub ReleaseRestore(timeOut As Integer)
      Dim lastWriter As Integer 

      Try
         rwl.AcquireReaderLock(timeOut)
         Try 
            ' It's safe for this thread to read from the shared resource, 
            ' so read and cache the resource value. 
            Dim resourceValue As Integer = resource
            Display("reads resource value " & resourceValue)
            Interlocked.Increment(reads)

            ' Save the current writer sequence number.
            lastWriter = rwl.WriterSeqNum

            ' Release the lock and save a cookie so the lock can be restored later. 
            Dim lc As LockCookie = rwl.ReleaseLock()

            ' Wait for a random interval and then restore the previous state of the lock.
            Thread.Sleep(rnd.Next(250))
            rwl.RestoreLock(lc)

            ' Check whether other threads obtained the writer lock in the interval. 
            ' If not, then the cached value of the resource is still valid. 
            If rwl.AnyWritersSince(lastWriter) Then
               resourceValue = resource
               Interlocked.Increment(reads)
               Display("resource has changed " & resourceValue)
            Else
               Display("resource has not changed " & resourceValue)
            End If 
         Finally 
            ' Ensure that the lock is released.
            rwl.ReleaseReaderLock()
         End Try 
      Catch ex As ApplicationException
         ' The reader lock request timed out.
         Interlocked.Increment(readerTimeouts)
      End Try 
   End Sub 

   ' Helper method briefly displays the most recent thread action. 
   Sub Display(msg As String)
      Console.Write("Thread {0} {1}.       " & vbCr, Thread.CurrentThread.Name, msg)
   End Sub 
End Module

.NET Framework

Supported in: 4.6, 4.5, 4, 3.5, 3.0, 2.0, 1.1

.NET Framework Client Profile

Supported in: 4, 3.5 SP1

This type is thread safe.

Show:
© 2015 Microsoft