ReaderWriterLock Classe
Definizione
Importante
Alcune informazioni sono relative alla release non definitiva del prodotto, che potrebbe subire modifiche significative prima della release definitiva. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.
Definisce un blocco che supporta singoli writer e più lettori.
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
- Ereditarietà
- Ereditarietà
-
ReaderWriterLock
- Attributi
Esempio
Nell'esempio seguente viene illustrato come usare un ReaderWriterLock oggetto per proteggere una risorsa condivisa, un valore intero denominato resource, che viene letto simultaneamente e scritto esclusivamente da più thread. Si noti che l'oggetto ReaderWriterLock viene dichiarato a livello di classe in modo che sia visibile a tutti i thread.
// 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
Commenti
Importante
Il framework di .NET include due blocchi reader-writer, ReaderWriterLockSlim e ReaderWriterLock. ReaderWriterLockSlim è consigliato per tutti i nuovi sviluppi. ReaderWriterLockSlim è simile a ReaderWriterLock, ma ha regole semplificate per la ricorsione e per l'aggiornamento e il downgrade dello stato di blocco. ReaderWriterLockSlim evita molti casi di potenziale deadlock. Inoltre, le prestazioni di ReaderWriterLockSlim sono significativamente migliori di ReaderWriterLock.
ReaderWriterLock viene usato per sincronizzare l'accesso a una risorsa. In qualsiasi momento, consente l'accesso in lettura simultaneo per più thread o l'accesso in scrittura per un singolo thread. In una situazione in cui una risorsa viene modificata raramente, offre una ReaderWriterLock velocità effettiva migliore rispetto a un semplice blocco una alla volta, ad esempio Monitor.
ReaderWriterLock funziona meglio dove la maggior parte degli accessi sono le letture, mentre le scritture sono poco frequenti e di breve durata. Più lettori si alternano con singoli scrittori, in modo che né lettori né scrittori siano bloccati per lunghi periodi.
Note
Il blocco del lettore o i blocchi del writer per lunghi periodi di fame bloccherà altri thread. Per ottenere prestazioni ottimali, prendere in considerazione la ristrutturazione dell'applicazione per ridurre al minimo la durata delle scritture.
Un thread può contenere un blocco lettore o un blocco writer, ma non entrambi contemporaneamente. Anziché rilasciare un blocco lettore per acquisire il blocco writer, è possibile usare UpgradeToWriterLock e DowngradeFromWriterLock.
Le richieste di blocco ricorsive aumentano il numero di blocchi in un blocco.
I lettori e i writer vengono accodati separatamente. Quando un thread rilascia il blocco writer, a tutti i thread in attesa nella coda del lettore in quel momento vengono concessi blocchi lettore; quando tutti i blocchi del lettore sono stati rilasciati, al thread successivo in attesa nella coda del writer, se presente, viene concesso il blocco del writer e così via. In altre parole, ReaderWriterLock alterna una raccolta di lettori e un writer.
Mentre un thread nella coda del writer è in attesa del rilascio dei blocchi del lettore attivo, i thread che richiedono nuovi blocchi lettore si accumulano nella coda del lettore. Le richieste non vengono concesse, anche se potrebbero condividere l'accesso simultaneo con i titolari di blocco lettore esistenti; ciò consente di proteggere i writer da blocchi indefiniti da parte dei lettori.
La maggior parte dei metodi per l'acquisizione di blocchi su un ReaderWriterLock accettare valori di timeout. Usare i timeout per evitare deadlock nell'applicazione. Ad esempio, un thread potrebbe acquisire il blocco writer su una risorsa e quindi richiedere un blocco lettore su una seconda risorsa; nel frattempo, un altro thread potrebbe acquisire il blocco writer sulla seconda risorsa e richiedere un blocco lettore sul primo. A meno che non vengano usati timeout, il deadlock dei thread.
Se l'intervallo di timeout scade e la richiesta di blocco non è stata concessa, il metodo restituisce il controllo al thread chiamante generando un'eccezione ApplicationException. Un thread può intercettare questa eccezione e determinare l'azione da eseguire successivamente.
I timeout sono espressi in millisecondi. Se si usa un System.TimeSpan oggetto per specificare il timeout, il valore utilizzato è il numero totale di millisecondi interi rappresentati da TimeSpan. Nella tabella seguente vengono illustrati i valori di timeout validi in millisecondi.
| Value | Descrizione |
|---|---|
| -1 | Il thread attende fino all'acquisizione del blocco, indipendentemente dal tempo necessario. Per i metodi che specificano timeout integer, è possibile usare la costante Infinite . |
| 0 | Il thread non attende di acquisire il blocco. Se il blocco non può essere acquisito immediatamente, il metodo restituisce . |
| >0 | Numero di millisecondi di attesa. |
Ad eccezione di -1, i valori di timeout negativi non sono consentiti. Se si specifica un numero intero negativo diverso da -1, viene invece usato un valore di timeout pari a zero. Ovvero, il metodo restituisce senza attendere, se il blocco non può essere acquisito immediatamente. Se si specifica un oggetto TimeSpan che rappresenta un numero negativo di millisecondi diversi da -1, ArgumentOutOfRangeException viene generata un'eccezione.
Costruttori
| Nome | Descrizione |
|---|---|
| ReaderWriterLock() |
Inizializza una nuova istanza della classe ReaderWriterLock. |
Proprietà
| Nome | Descrizione |
|---|---|
| IsReaderLockHeld |
Ottiene un valore che indica se il thread corrente contiene un blocco lettore. |
| IsWriterLockHeld |
Ottiene un valore che indica se il thread corrente contiene il blocco writer. |
| WriterSeqNum |
Ottiene il numero di sequenza corrente. |
Metodi
| Nome | Descrizione |
|---|---|
| AcquireReaderLock(Int32) |
Acquisisce un blocco lettore, usando un Int32 valore per il timeout. |
| AcquireReaderLock(TimeSpan) |
Acquisisce un blocco lettore, usando un TimeSpan valore per il timeout. |
| AcquireWriterLock(Int32) |
Acquisisce il blocco writer, usando un Int32 valore per il timeout. |
| AcquireWriterLock(TimeSpan) |
Acquisisce il blocco writer, usando un TimeSpan valore per il timeout. |
| AnyWritersSince(Int32) |
Indica se il blocco writer è stato concesso a qualsiasi thread dopo che è stato ottenuto il numero di sequenza. |
| DowngradeFromWriterLock(LockCookie) |
Ripristina lo stato del blocco del thread in quello che era precedentemente UpgradeToWriterLock(Int32) chiamato. |
| Equals(Object) |
Determina se l'oggetto specificato è uguale all'oggetto corrente. (Ereditato da Object) |
| Finalize() |
Assicura che le risorse vengano liberate e che vengano eseguite altre operazioni di pulizia quando il Garbage Collector recupera l'oggetto ReaderWriterLock . |
| GetHashCode() |
Funge da funzione hash predefinita. (Ereditato da Object) |
| GetType() |
Ottiene il Type dell'istanza corrente. (Ereditato da Object) |
| MemberwiseClone() |
Crea una copia superficiale del Objectcorrente. (Ereditato da Object) |
| ReleaseLock() |
Rilascia il blocco, indipendentemente dal numero di volte in cui il thread ha acquisito il blocco. |
| ReleaseReaderLock() |
Consente di diminuire il conteggio dei blocchi. |
| ReleaseWriterLock() |
Decrementa il conteggio dei blocchi sul blocco writer. |
| RestoreLock(LockCookie) |
Ripristina lo stato del blocco del thread a quello che era prima di chiamare ReleaseLock(). |
| ToString() |
Restituisce una stringa che rappresenta l'oggetto corrente. (Ereditato da Object) |
| UpgradeToWriterLock(Int32) |
Aggiorna un blocco lettore al blocco writer, usando un Int32 valore per il timeout. |
| UpgradeToWriterLock(TimeSpan) |
Aggiorna un blocco lettore al blocco writer, usando un |
Si applica a
Thread safety
Questo tipo è thread-safe.
Vedi anche
- di threading gestito
- Readerwriterlock