ReaderWriterLock Klasse

Definition

Definiert eine Sperre, die einzelne Autoren und mehrere Leser unterstützt.

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
Vererbung
ReaderWriterLock
Vererbung
ReaderWriterLock
Attribute

Beispiele

Im folgenden Beispiel wird veranschaulicht, wie Sie eine ReaderWriterLock freigegebene Ressource, einen ganzzahligen Wert resourcenamens , verwenden, der gleichzeitig gelesen und exklusiv von mehreren Threads geschrieben wird. Beachten Sie, dass die Eigenschaft ReaderWriterLock auf Klassenebene deklariert wird, damit sie für alle Threads sichtbar ist.

// 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

Hinweise

Von Bedeutung

Das .NET Framework verfügt über zwei Reader-Writer-Sperren, ReaderWriterLockSlim und ReaderWriterLock. ReaderWriterLockSlim wird für alle neuen Entwicklungsprojekte empfohlen. ReaderWriterLockSlim ähnelt ReaderWriterLock, aber es hat vereinfachte Regeln für Rekursion sowie für das Aktualisieren und Herabstufen des Sperrzustands. ReaderWriterLockSlim verhindert viele der Fälle potenzieller Deadlock-Situationen. Darüber hinaus ist die Leistung von ReaderWriterLockSlim deutlich besser als die von ReaderWriterLock.

ReaderWriterLock wird verwendet, um den Zugriff auf eine Ressource zu synchronisieren. Zu einem bestimmten Zeitpunkt ermöglicht es entweder gleichzeitigen Lesezugriff für mehrere Threads oder Schreibzugriff für einen einzelnen Thread. In einer Situation, in der eine Ressource selten geändert wird, bietet ein ReaderWriterLock besserer Durchsatz als eine einfache 1:a-Zeit-Sperre, z Monitor. B. .

ReaderWriterLock funktioniert am besten, wenn die meisten Zugriffe gelesen werden, während Schreibvorgänge selten und von kurzer Dauer sind. Mehrere Leser wechseln mit einzelnen Autoren, sodass weder Leser noch Autoren für lange Zeiträume blockiert werden.

Note

Wenn Sie Lese- oder Schreibsperren für lange Zeiträume halten, werden andere Threads verhungern. Für eine optimale Leistung sollten Sie die Umstrukturierung Ihrer Anwendung in Betracht ziehen, um die Dauer von Schreibvorgängen zu minimieren.

Ein Thread kann eine Lese- oder Schreibsperre enthalten, aber nicht beide gleichzeitig. Anstatt eine Lesesperre freizugeben, um die Writer-Sperre zu erwerben, können Sie sie verwenden UpgradeToWriterLock und DowngradeFromWriterLock.

Rekursive Sperranforderungen erhöhen die Sperranzahl einer Sperre.

Leser und Autoren werden separat in die Warteschlange gestellt. Wenn ein Thread die Writer-Sperre loslässt, erhalten alle Threads, die in der Lesewarteschlange warten, sofort Lesesperren. Wenn alle diese Lesesperren freigegeben wurden, wird der nächste Thread, der in der Writer-Warteschlange wartet, falls vorhanden, die Writer-Sperre gewährt usw. Anders ausgedrückt: ReaderWriterLock Wechselt zwischen einer Sammlung von Lesern und einem Schriftsteller.

Während ein Thread in der Writer-Warteschlange darauf wartet, dass aktive Lesesperren freigegeben werden, sammeln sich Threads, die neue Lesesperren anfordern, in der Lesewarteschlange an. Ihre Anträge werden nicht gewährt, auch wenn sie gleichzeitigen Zugriff mit bestehenden Lesesperrhaltern teilen könnten; dadurch können Autoren vor unbestimmter Blockierung durch Leser geschützt werden.

Die meisten Methoden zum Abrufen von Sperren für ein ReaderWriterLock Akzeptieren von Timeoutwerten. Verwenden Sie Timeouts, um Deadlocks in Ihrer Anwendung zu vermeiden. Beispielsweise kann ein Thread die Writer-Sperre für eine Ressource abrufen und dann eine Leseberechtigung für eine zweite Ressource anfordern. In der Zwischenzeit kann ein anderer Thread die Writer-Sperre für die zweite Ressource abrufen und eine Leseberechtigung für den ersten anfordern. Es sei denn, Timeouts werden verwendet, die Threads deadlock.

Wenn das Timeoutintervall abläuft und die Sperranforderung nicht gewährt wurde, gibt die Methode die Steuerung an den aufrufenden Thread zurück, indem ein Ausgelöst wird ApplicationException. Ein Thread kann diese Ausnahme abfangen und bestimmen, welche Aktion als Nächstes ausgeführt werden soll.

Timeouts werden in Millisekunden ausgedrückt. Wenn Sie ein System.TimeSpan Timeout angeben, ist der verwendete Wert die Gesamtanzahl der ganzen Millisekunden, die durch die TimeSpan. Die folgende Tabelle zeigt die gültigen Timeoutwerte in Millisekunden.

Value Description
-1 Der Thread wartet, bis die Sperre abgerufen wird, unabhängig davon, wie lange es dauert. Bei Methoden, die ganzzahlige Timeouts angeben, kann die Konstante Infinite verwendet werden.
0 Der Thread wartet nicht auf den Erwerb der Sperre. Wenn die Sperre nicht sofort abgerufen werden kann, wird die Methode zurückgegeben.
>0 Die Anzahl der zu wartenden Millisekunden.

Mit Ausnahme von -1 sind negative Timeoutwerte nicht zulässig. Wenn Sie eine andere negative ganze Zahl als -1 angeben, wird stattdessen ein Timeoutwert von Null verwendet. (Das heißt, die Methode gibt ohne Warten zurück, wenn die Sperre nicht sofort abgerufen werden kann.) Wenn Sie einen TimeSpan Wert angeben, der eine negative Anzahl anderer Millisekunden als -1 darstellt, ArgumentOutOfRangeException wird ausgelöst.

Konstruktoren

Name Beschreibung
ReaderWriterLock()

Initialisiert eine neue Instanz der ReaderWriterLock-Klasse.

Eigenschaften

Name Beschreibung
IsReaderLockHeld

Ruft einen Wert ab, der angibt, ob der aktuelle Thread eine Lesesperre enthält.

IsWriterLockHeld

Ruft einen Wert ab, der angibt, ob der aktuelle Thread die Writer-Sperre enthält.

WriterSeqNum

Ruft die aktuelle Sequenznummer ab.

Methoden

Name Beschreibung
AcquireReaderLock(Int32)

Dient zum Abrufen einer Lesesperre mit einem Int32 Wert für das Timeout.

AcquireReaderLock(TimeSpan)

Dient zum Abrufen einer Lesesperre mit einem TimeSpan Wert für das Timeout.

AcquireWriterLock(Int32)

Erwirbt die Writer-Sperre unter Verwendung eines Int32 Werts für das Timeout.

AcquireWriterLock(TimeSpan)

Erwirbt die Writer-Sperre unter Verwendung eines TimeSpan Werts für das Timeout.

AnyWritersSince(Int32)

Gibt an, ob die Writer-Sperre einem Thread gewährt wurde, seit die Sequenznummer abgerufen wurde.

DowngradeFromWriterLock(LockCookie)

Stellt den Sperrstatus des Threads auf den UpgradeToWriterLock(Int32) zuvor aufgerufenen Thread wieder her.

Equals(Object)

Bestimmt, ob das angegebene Objekt dem aktuellen Objekt entspricht.

(Geerbt von Object)
Finalize()

Stellt sicher, dass Ressourcen freigegeben werden und andere Bereinigungsvorgänge ausgeführt werden, wenn der Garbage Collector das ReaderWriterLock Objekt zurückgibt.

GetHashCode()

Dient als Standardhashfunktion.

(Geerbt von Object)
GetType()

Ruft die Type der aktuellen Instanz ab.

(Geerbt von Object)
MemberwiseClone()

Erstellt eine flache Kopie der aktuellen Object.

(Geerbt von Object)
ReleaseLock()

Gibt die Sperre unabhängig davon frei, wie oft der Thread die Sperre abgerufen hat.

ReleaseReaderLock()

Verringert die Sperrenanzahl.

ReleaseWriterLock()

Erhöht die Sperranzahl für die Writer-Sperre.

RestoreLock(LockCookie)

Stellt den Sperrstatus des Threads vor dem Aufrufen ReleaseLock()wieder her.

ToString()

Gibt eine Zeichenfolge zurück, die das aktuelle Objekt darstellt.

(Geerbt von Object)
UpgradeToWriterLock(Int32)

Aktualisiert eine Lesesperre auf die Writer-Sperre, wobei ein Int32 Wert für das Timeout verwendet wird.

UpgradeToWriterLock(TimeSpan)

Aktualisiert eine Lesesperre auf die Writer-Sperre, wobei ein TimeSpan Wert für das Timeout verwendet wird.

Gilt für:

Threadsicherheit

Dieser Typ ist threadsicher.

Weitere Informationen