ReaderWriterLock Класс

Определение

Определяет блокировку, которая поддерживает отдельные записи и несколько средств чтения.

public ref class ReaderWriterLock sealed : System::Runtime::ConstrainedExecution::CriticalFinalizerObject
public ref class ReaderWriterLock sealed
public sealed class ReaderWriterLock : System.Runtime.ConstrainedExecution.CriticalFinalizerObject
public sealed class ReaderWriterLock
[System.Runtime.InteropServices.ComVisible(true)]
public sealed class ReaderWriterLock : System.Runtime.ConstrainedExecution.CriticalFinalizerObject
type ReaderWriterLock = class
    inherit CriticalFinalizerObject
type ReaderWriterLock = class
[<System.Runtime.InteropServices.ComVisible(true)>]
type ReaderWriterLock = class
    inherit CriticalFinalizerObject
Public NotInheritable Class ReaderWriterLock
Inherits CriticalFinalizerObject
Public NotInheritable Class ReaderWriterLock
Наследование
ReaderWriterLock
Наследование
ReaderWriterLock
Атрибуты

Примеры

В следующем примере показано, как использовать ReaderWriterLock для защиты общего ресурса целочисленное значение с именем resource, которое одновременно считывается и записывается исключительно несколькими потоками. Обратите внимание, что объявлен на ReaderWriterLock уровне класса, чтобы он был видим для всех потоков.

// The complete code is located in the ReaderWriterLock class topic.
using System;
using System.Threading;

public class Example
{
   static ReaderWriterLock rwl = new ReaderWriterLock();
   // Define the shared resource protected by the ReaderWriterLock.
   static int resource = 0;

   const int numThreads = 26;
   static bool running = true;

   // Statistics.
   static int readerTimeouts = 0;
   static int writerTimeouts = 0;
   static int reads = 0;
   static int writes = 0;

   public static void Main()
   {
      // Start a series of threads to randomly read from and
      // write to the shared resource.
      Thread[] t = new Thread[numThreads];
      for (int i = 0; i < numThreads; i++){
         t[i] = new Thread(new ThreadStart(ThreadProc));
         t[i].Name = new String((char)(i + 65), 1);
         t[i].Start();
         if (i > 10)
            Thread.Sleep(300);
      }

      // Tell the threads to shut down and wait until they all finish.
      running = false;
      for (int i = 0; i < numThreads; i++)
         t[i].Join();

      // Display statistics.
      Console.WriteLine("\n{0} reads, {1} writes, {2} reader time-outs, {3} writer time-outs.",
            reads, writes, readerTimeouts, writerTimeouts);
      Console.Write("Press ENTER to exit... ");
      Console.ReadLine();
   }

   static void ThreadProc()
   {
      Random rnd = new Random();

      // Randomly select a way for the thread to read and write from the shared
      // resource.
      while (running) {
         double action = rnd.NextDouble();
         if (action < .8)
            ReadFromResource(10);
         else if (action < .81)
            ReleaseRestore(rnd, 50);
         else if (action < .90)
            UpgradeDowngrade(rnd, 100);
         else
            WriteToResource(rnd, 100);
      }
   }

   // Request and release a reader lock, and handle time-outs.
   static void ReadFromResource(int timeOut)
   {
      try {
         rwl.AcquireReaderLock(timeOut);
         try {
            // It is safe for this thread to read from the shared resource.
            Display("reads resource value " + resource);
            Interlocked.Increment(ref reads);
         }
         finally {
            // Ensure that the lock is released.
            rwl.ReleaseReaderLock();
         }
      }
      catch (ApplicationException) {
         // The reader lock request timed out.
         Interlocked.Increment(ref readerTimeouts);
      }
   }

   // Request and release the writer lock, and handle time-outs.
   static void WriteToResource(Random rnd, int timeOut)
   {
      try {
         rwl.AcquireWriterLock(timeOut);
         try {
            // It's safe for this thread to access from the shared resource.
            resource = rnd.Next(500);
            Display("writes resource value " + resource);
            Interlocked.Increment(ref writes);
         }
         finally {
            // Ensure that the lock is released.
            rwl.ReleaseWriterLock();
         }
      }
      catch (ApplicationException) {
         // The writer lock request timed out.
         Interlocked.Increment(ref writerTimeouts);
      }
   }

   // Requests a reader lock, upgrades the reader lock to the writer
   // lock, and downgrades it to a reader lock again.
   static void UpgradeDowngrade(Random rnd, int timeOut)
   {
      try {
         rwl.AcquireReaderLock(timeOut);
         try {
            // It's safe for this thread to read from the shared resource.
            Display("reads resource value " + resource);
            Interlocked.Increment(ref 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 {
               LockCookie lc = 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(ref writes);
               }
               finally {
                  // Ensure that the lock is released.
                  rwl.DowngradeFromWriterLock(ref lc);
               }
            }
            catch (ApplicationException) {
               // The upgrade request timed out.
               Interlocked.Increment(ref writerTimeouts);
            }

            // If the lock was downgraded, it's still safe to read from the resource.
            Display("reads resource value " + resource);
            Interlocked.Increment(ref reads);
         }
         finally {
            // Ensure that the lock is released.
            rwl.ReleaseReaderLock();
         }
      }
      catch (ApplicationException) {
         // The reader lock request timed out.
         Interlocked.Increment(ref readerTimeouts);
      }
   }

   // 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.
   static void ReleaseRestore(Random rnd, int timeOut)
   {
      int lastWriter;

      try {
         rwl.AcquireReaderLock(timeOut);
         try {
            // It's safe for this thread to read from the shared resource,
            // so read and cache the resource value.
            int resourceValue = resource;     // Cache the resource value.
            Display("reads resource value " + resourceValue);
            Interlocked.Increment(ref reads);

            // Save the current writer sequence number.
            lastWriter = rwl.WriterSeqNum;

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

            // Wait for a random interval and then restore the previous state of the lock.
            Thread.Sleep(rnd.Next(250));
            rwl.RestoreLock(ref 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)) {
               resourceValue = resource;
               Interlocked.Increment(ref reads);
               Display("resource has changed " + resourceValue);
            }
            else {
               Display("resource has not changed " + resourceValue);
            }
         }
         finally {
            // Ensure that the lock is released.
            rwl.ReleaseReaderLock();
         }
      }
      catch (ApplicationException) {
         // The reader lock request timed out.
         Interlocked.Increment(ref readerTimeouts);
      }
   }

   // Helper method briefly displays the most recent thread action.
   static void Display(string msg)
   {
      Console.Write("Thread {0} {1}.       \r", Thread.CurrentThread.Name, msg);
   }
}
' 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
   
   ' 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()
      Dim rnd As New Random

      ' 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(rnd, 50)
         ElseIf action < 0.9 Then
            UpgradeDowngrade(rnd, 100)
         Else
            WriteToResource(rnd, 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(rnd As Random, 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(rnd As Random, 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(rnd As Random ,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 имеет две блокировки записи чтения, ReaderWriterLockSlim и ReaderWriterLock. ReaderWriterLockSlim рекомендуется для всех новых разработок. ReaderWriterLockSlim аналогичен ReaderWriterLock, но он имеет упрощенные правила для рекурсии и обновления и понижения состояния блокировки. ReaderWriterLockSlim избегает многих случаев потенциальной взаимоблокировки. Кроме того, производительность ReaderWriterLockSlim значительно выше ReaderWriterLock.

ReaderWriterLock используется для синхронизации доступа к ресурсу. В любой момент он разрешает одновременный доступ на чтение для нескольких потоков или доступ на запись для одного потока. В ситуации, когда ресурс изменяется редко, обеспечивает лучшую пропускную способность, ReaderWriterLock чем простая блокировка за один раз, например Monitor.

ReaderWriterLock лучше всего работает, когда большинство операций доступа являются чтением, в то время как записи нечасто и короткой длительности. Несколько читателей чередуются с одними писателями, чтобы ни читатели, ни писатели не блокировались в течение длительных периодов.

Note

Хранение блокировок чтения или блокировок записи в течение длительного периода приведет к нехватке других потоков. Для повышения производительности рекомендуется перенастроить приложение, чтобы свести к минимуму продолжительность записи.

Поток может содержать блокировку чтения или блокировку записи, но не одновременно. Вместо освобождения блокировки чтения для получения блокировки записи можно использовать UpgradeToWriterLock и DowngradeFromWriterLock.

Рекурсивные запросы на блокировку увеличивают количество блокировок.

Читатели и записи помещаются в очередь отдельно. Когда поток освобождает блокировку записи, все потоки, ожидающие в очереди чтения в этом моменте, предоставляются блокировки чтения; когда все эти блокировки чтения были освобождены, следующий поток, ожидая в очереди записи, если таковые имеются, предоставляется блокировка записи и т. д. Другими словами, ReaderWriterLock чередуются между коллекцией читателей и одним писателем.

Хотя поток в очереди записи ожидает освобождения активных блокировок чтения, потоки, запрашивающие новые блокировки чтения, накапливаются в очереди чтения. Их запросы не предоставляются, даже если они могут совместно использовать одновременный доступ с существующими владельцами блокировки чтения; это помогает защитить писателей от неограниченного блокирования читателями.

Большинство методов получения блокировок для принятия значений ReaderWriterLock времени ожидания. Используйте время ожидания, чтобы избежать взаимоблокировок в приложении. Например, поток может получить блокировку записи для одного ресурса, а затем запросить блокировку чтения для второго ресурса; в то же время другой поток может получить блокировку записи во втором ресурсе и запросить блокировку чтения в первую очередь. Если время ожидания не используется, потоки взаимоблокировки.

Если истекает интервал времени ожидания и запрос блокировки не предоставлен, метод возвращает управление вызывающему потоку путем вызова ApplicationException. Поток может перехватывать это исключение и определять, какие действия следует предпринять далее.

Время ожидания выражается в миллисекундах. Если вы используете System.TimeSpan для указания времени ожидания, используемое значение — общее количество целых миллисекундах, представленных параметром TimeSpan. В следующей таблице показаны допустимые значения времени ожидания в миллисекундах.

Value Description
-1 Поток ожидает, пока блокировка не будет получена, независимо от того, сколько времени она занимает. Для методов, определяющих время ожидания целых чисел, можно использовать константу Infinite .
0 Поток не ожидает получения блокировки. Если блокировка не может быть получена немедленно, метод возвращается.
>0 Количество миллисекунда для ожидания.

За исключением -1 отрицательные значения времени ожидания не допускаются. Если указать отрицательное целое число, отличное от -1, используется значение времени ожидания нуля. (То есть метод возвращается без ожидания, если блокировка не может быть получена немедленно.) Если указать TimeSpan значение, представляющее отрицательное число миллисекунда, отличное от -1, ArgumentOutOfRangeException создается.

Конструкторы

Имя Описание
ReaderWriterLock()

Инициализирует новый экземпляр класса ReaderWriterLock.

Свойства

Имя Описание
IsReaderLockHeld

Возвращает значение, указывающее, содержит ли текущий поток блокировку чтения.

IsWriterLockHeld

Возвращает значение, указывающее, содержит ли текущий поток блокировку записи.

WriterSeqNum

Возвращает текущий номер последовательности.

Методы

Имя Описание
AcquireReaderLock(Int32)

Получает блокировку Int32 чтения, используя значение времени ожидания.

AcquireReaderLock(TimeSpan)

Получает блокировку чтения, используя TimeSpan значение времени ожидания.

AcquireWriterLock(Int32)

Получает блокировку Int32 записи, используя значение времени ожидания.

AcquireWriterLock(TimeSpan)

Получает блокировку TimeSpan записи, используя значение времени ожидания.

AnyWritersSince(Int32)

Указывает, предоставлена ли блокировка записи любому потоку после получения номера последовательности.

DowngradeFromWriterLock(LockCookie)

Восстанавливает состояние блокировки потока до UpgradeToWriterLock(Int32) вызова.

Equals(Object)

Определяет, равен ли указанный объект текущему объекту.

(Унаследовано от Object)
Finalize()

Гарантирует, что ресурсы освобождены и другие операции очистки выполняются при освобождении ReaderWriterLock объекта сборщиком мусора.

GetHashCode()

Служит хэш-функцией по умолчанию.

(Унаследовано от Object)
GetType()

Возвращает Type текущего экземпляра.

(Унаследовано от Object)
MemberwiseClone()

Создает неглубокую копию текущей Object.

(Унаследовано от Object)
ReleaseLock()

Освобождает блокировку независимо от количества операций, когда поток получил блокировку.

ReleaseReaderLock()

Уменьшает на единицу счетчик блокировок.

ReleaseWriterLock()

Уменьшает количество блокировок на блокировке записи.

RestoreLock(LockCookie)

Восстанавливает состояние блокировки потока до ReleaseLock()вызова.

ToString()

Возвращает строку, представляющую текущий объект.

(Унаследовано от Object)
UpgradeToWriterLock(Int32)

Обновляет блокировку чтения до блокировки записи, используя Int32 значение времени ожидания.

UpgradeToWriterLock(TimeSpan)

Обновляет блокировку чтения до блокировки записи, используя TimeSpan значение времени ожидания.

Применяется к

Потокобезопасность

Этот тип является потокобезопасной.

См. также раздел