ReaderWriterLock Classe
Définition
Important
Certaines informations portent sur la préversion du produit qui est susceptible d’être en grande partie modifiée avant sa publication. Microsoft exclut toute garantie, expresse ou implicite, concernant les informations fournies ici.
Définit un verrou qui prend en charge des enregistreurs uniques et plusieurs lecteurs.
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
- Héritage
- Héritage
-
ReaderWriterLock
- Attributs
Exemples
L’exemple suivant montre comment utiliser un ReaderWriterLock pour protéger une ressource partagée, une valeur entière nommée resource, qui est lue simultanément et écrite exclusivement par plusieurs threads. Notez que l’objet ReaderWriterLock est déclaré au niveau de la classe afin qu’il soit visible par tous les threads.
// 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
Remarques
Important
Le framework .NET a deux verrous d’enregistreur de lecture, ReaderWriterLockSlim et ReaderWriterLock. ReaderWriterLockSlim est recommandé pour tout nouveau développement. ReaderWriterLockSlim est similaire à ReaderWriterLock, mais il a simplifié les règles de récursivité et de mise à niveau et de rétrogradation de l’état de verrouillage. ReaderWriterLockSlim évite de nombreux cas d’interblocage potentiel. En outre, la performance de ReaderWriterLockSlim est nettement meilleure que celle de ReaderWriterLock.
ReaderWriterLock est utilisé pour synchroniser l’accès à une ressource. À tout moment, il autorise l’accès en lecture simultané pour plusieurs threads ou l’accès en écriture pour un seul thread. Dans une situation où une ressource est rarement modifiée, un ReaderWriterLock débit est plus élevé qu’un simple verrou un-à-temps, tel que Monitor.
ReaderWriterLock fonctionne mieux où la plupart des accès sont des lectures, tandis que les écritures sont peu fréquentes et de courte durée. Plusieurs lecteurs alternent avec des écrivains uniques, afin que ni les lecteurs ni les écrivains ne soient bloqués pendant de longues périodes.
Note
La conservation des verrous de lecteur ou des verrous d’enregistreur pendant de longues périodes manquera d’autres threads. Pour optimiser les performances, envisagez de restructurer votre application pour réduire la durée des écritures.
Un thread peut contenir un verrou de lecteur ou un verrou d’enregistreur, mais pas les deux en même temps. Au lieu de libérer un verrou de lecteur pour acquérir le verrou writer, vous pouvez utiliser UpgradeToWriterLock et DowngradeFromWriterLock.
Les demandes de verrou récursive augmentent le nombre de verrous sur un verrou.
Les lecteurs et les enregistreurs sont mis en file d’attente séparément. Lorsqu’un thread libère le verrou de l’enregistreur, tous les threads en attente dans la file d’attente du lecteur à cet instant reçoivent des verrous de lecteur ; lorsque tous ces verrous de lecteur ont été libérés, le thread suivant en attente dans la file d’attente de l’enregistreur, le cas échéant, est accordé au verrou de l’enregistreur, et ainsi de suite. En d’autres termes, ReaderWriterLock alterne entre une collection de lecteurs et un écrivain.
Alors qu’un thread dans la file d’attente de l’enregistreur attend que les verrous de lecteur actifs soient libérés, les threads demandant de nouveaux verrous de lecteur s’accumulent dans la file d’attente du lecteur. Leurs demandes ne sont pas accordées, même si elles peuvent partager l’accès simultané avec les titulaires de verrous de lecture existants ; cela permet de protéger les écrivains contre les blocages indéfinis par les lecteurs.
La plupart des méthodes d’acquisition de verrous sur des valeurs de délai d’attente ReaderWriterLock d’acceptation. Utilisez des délais d’attente pour éviter les blocages dans votre application. Par exemple, un thread peut acquérir le verrou writer sur une ressource, puis demander un verrou de lecteur sur une deuxième ressource ; en attendant, un autre thread peut acquérir le verrou de l’enregistreur sur la deuxième ressource et demander un verrou de lecteur sur le premier. Sauf si des délais d’attente sont utilisés, le blocage des threads.
Si l’intervalle de délai d’expiration expire et que la demande de verrouillage n’a pas été accordée, la méthode retourne le contrôle au thread appelant en lève un ApplicationException. Un thread peut intercepter cette exception et déterminer l’action à entreprendre ensuite.
Les délais d’attente sont exprimés en millisecondes. Si vous utilisez un System.TimeSpan pour spécifier le délai d’attente, la valeur utilisée est le nombre total de millisecondes entières représentées par le TimeSpan. Le tableau suivant présente les valeurs de délai d’attente valides en millisecondes.
| Value | Description |
|---|---|
| -1 | Le thread attend que le verrou soit acquis, quel que soit le temps nécessaire. Pour les méthodes qui spécifient des délais d’attente entiers, la constante Infinite peut être utilisée. |
| 0 | Le thread n’attend pas d’acquérir le verrou. Si le verrou ne peut pas être acquis immédiatement, la méthode retourne. |
| >0 | Nombre de millisecondes à attendre. |
À l’exception de -1, les valeurs de délai d’attente négatives ne sont pas autorisées. Si vous spécifiez un entier négatif autre que -1, une valeur de délai d’attente de zéro est utilisée à la place. (Autrement dit, la méthode retourne sans attendre, si le verrou ne peut pas être acquis immédiatement.) Si vous spécifiez un TimeSpan nombre négatif de millisecondes autres que -1, ArgumentOutOfRangeException est levée.
Constructeurs
| Nom | Description |
|---|---|
| ReaderWriterLock() |
Initialise une nouvelle instance de la classe ReaderWriterLock. |
Propriétés
| Nom | Description |
|---|---|
| IsReaderLockHeld |
Obtient une valeur indiquant si le thread actuel contient un verrou de lecteur. |
| IsWriterLockHeld |
Obtient une valeur indiquant si le thread actuel contient le verrou de l’enregistreur. |
| WriterSeqNum |
Obtient le numéro de séquence actuel. |
Méthodes
| Nom | Description |
|---|---|
| AcquireReaderLock(Int32) |
Acquiert un verrou de lecteur à l’aide d’une Int32 valeur pour le délai d’attente. |
| AcquireReaderLock(TimeSpan) |
Acquiert un verrou de lecteur à l’aide d’une TimeSpan valeur pour le délai d’attente. |
| AcquireWriterLock(Int32) |
Acquiert le verrou de l’enregistreur à l’aide d’une Int32 valeur pour le délai d’attente. |
| AcquireWriterLock(TimeSpan) |
Acquiert le verrou de l’enregistreur à l’aide d’une TimeSpan valeur pour le délai d’attente. |
| AnyWritersSince(Int32) |
Indique si le verrou de l’enregistreur a été accordé à n’importe quel thread depuis l’obtention du numéro de séquence. |
| DowngradeFromWriterLock(LockCookie) |
Restaure l’état de verrouillage du thread sur ce qu’il était avant UpgradeToWriterLock(Int32) d’être appelé. |
| Equals(Object) |
Détermine si l’objet spécifié est égal à l’objet actuel. (Hérité de Object) |
| Finalize() |
Garantit que les ressources sont libérées et que d’autres opérations de nettoyage sont effectuées lorsque le garbage collector récupère l’objet ReaderWriterLock . |
| GetHashCode() |
Sert de fonction de hachage par défaut. (Hérité de Object) |
| GetType() |
Obtient la Type de l’instance actuelle. (Hérité de Object) |
| MemberwiseClone() |
Crée une copie superficielle du Objectactuel. (Hérité de Object) |
| ReleaseLock() |
Libère le verrou, quel que soit le nombre de fois où le thread a acquis le verrou. |
| ReleaseReaderLock() |
Décrémente le nombre de verrous. |
| ReleaseWriterLock() |
Décrémente le nombre de verrous sur le verrou de l’enregistreur. |
| RestoreLock(LockCookie) |
Restaure l’état du verrou du thread sur ce qu’il était avant d’appeler ReleaseLock(). |
| ToString() |
Retourne une chaîne qui représente l’objet actuel. (Hérité de Object) |
| UpgradeToWriterLock(Int32) |
Met à niveau un verrou de lecteur vers le verrou de l’enregistreur, en utilisant une Int32 valeur pour le délai d’attente. |
| UpgradeToWriterLock(TimeSpan) |
Met à niveau un verrou de lecteur vers le verrou de l’enregistreur, en utilisant une |
S’applique à
Cohérence de thread
Ce type est thread safe.