ReaderWriterLock Klasa
Definicja
Ważne
Niektóre informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany przed wydaniem. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Definiuje blokadę, która obsługuje pojedynczych składników zapisywania i wielu czytelników.
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
- Dziedziczenie
- Dziedziczenie
-
ReaderWriterLock
- Atrybuty
Przykłady
W poniższym przykładzie pokazano, jak używać elementu ReaderWriterLock do ochrony zasobu udostępnionego, wartości całkowitej o nazwie resource, która jest odczytywana jednocześnie i zapisywana wyłącznie przez wiele wątków. Należy pamiętać, że element ReaderWriterLock jest zadeklarowany na poziomie klasy, aby był widoczny dla wszystkich wątków.
// 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
Uwagi
Ważna
Struktura .NET ma dwie blokady modułu zapisywania czytnika, ReaderWriterLockSlim i ReaderWriterLock. ReaderWriterLockSlim jest zalecana dla wszystkich nowych projektów. ReaderWriterLockSlim jest podobny do ReaderWriterLock, ale ma uproszczone reguły rekursji i uaktualniania i obniżania stanu blokady. ReaderWriterLockSlim unika wielu przypadków potencjalnego zakleszczenia. Ponadto wydajność funkcji ReaderWriterLockSlim jest znacznie lepsza niż ReaderWriterLock.
ReaderWriterLock służy do synchronizowania dostępu do zasobu. W dowolnym momencie umożliwia równoczesny dostęp do odczytu dla wielu wątków lub dostęp do zapisu dla jednego wątku. W sytuacji, gdy zasób jest zmieniany rzadko, ReaderWriterLock zapewnia lepszą przepływność niż prosta blokada jednorazowa, taka jak Monitor.
ReaderWriterLock działa najlepiej, gdy większość dostępu jest odczytanych, podczas gdy zapisy są rzadko i krótki czas trwania. Wielu czytelników alternatywnych z pojedynczymi pisarzami, tak aby ani czytelnicy, ani pisarze nie są blokowani przez długi czas.
Uwaga / Notatka
Trzymanie zamków czytnika lub blokad zapisywania przez długie okresy będzie głodne innych wątków. Aby uzyskać najlepszą wydajność, rozważ restrukturyzację aplikacji, aby zminimalizować czas trwania operacji zapisu.
Wątek może przechowywać blokadę czytnika lub blokadę modułu zapisującego, ale nie obie w tym samym czasie. Zamiast zwalniać blokadę czytnika w celu uzyskania blokady modułu zapisywania, można użyć poleceń UpgradeToWriterLock i DowngradeFromWriterLock.
Rekursywne żądania blokady zwiększają liczbę blokad.
Czytelnicy i autorzy są kolejkowane oddzielnie. Gdy wątek zwalnia blokadę modułu zapisywania, wszystkie wątki oczekujące w kolejce czytelnika w tym momencie otrzymują blokady czytnika; gdy wszystkie te blokady czytnika zostały zwolnione, następny wątek czeka w kolejce modułu zapisywania, jeśli istnieje, otrzymuje blokadę modułu zapisywania itd. Innymi słowy, ReaderWriterLock alternatywy między kolekcją czytelników i jednym pisarzem.
Podczas gdy wątek w kolejce modułu zapisywania oczekuje na zwolnienie aktywnych blokad czytnika, wątki żądające nowych blokad czytnika gromadzą się w kolejce czytelnika. Ich żądania nie są przyznawane, mimo że mogą współdzielić współbieżny dostęp z istniejącymi posiadaczami blokady czytnika; Pomaga to chronić pisarzy przed nieokreślonym zablokowaniem przez czytelników.
Większość metod uzyskiwania blokad dla akceptowanych ReaderWriterLock wartości limitu czasu. Użyj limitów czasu, aby uniknąć zakleszczenia w aplikacji. Na przykład wątek może uzyskać blokadę modułu zapisywania w jednym zasobie, a następnie zażądać blokady czytnika w drugim zasobie; w międzyczasie inny wątek może uzyskać blokadę modułu zapisywania w drugim zasobie i zażądać blokady czytnika na pierwszym. Jeśli nie są używane limity czasu, zakleszczenia wątków.
Jeśli interwał limitu czasu wygaśnie i żądanie blokady nie zostało przyznane, metoda zwraca kontrolę do wątku wywołującego, zgłaszając element ApplicationException. Wątek może przechwycić ten wyjątek i określić, jaką akcję należy wykonać dalej.
Limity czasu są wyrażane w milisekundach. Jeśli używasz elementu System.TimeSpan , aby określić limit czasu, użyta wartość to całkowita liczba milisekund reprezentowanych przez TimeSpanparametr . W poniższej tabeli przedstawiono prawidłowe wartości limitu czasu w milisekundach.
| Value | Description |
|---|---|
| -1 | Wątek czeka na uzyskanie blokady, niezależnie od tego, jak długo trwa. W przypadku metod określających limity czasu liczby całkowitej można użyć stałej Infinite . |
| 0 | Wątek nie czeka na uzyskanie blokady. Jeśli nie można uzyskać blokady natychmiast, metoda zwraca wartość . |
| >0 | Liczba milisekund do oczekiwania. |
Z wyjątkiem -1 ujemne wartości limitu czasu są niedozwolone. Jeśli określisz ujemną liczbę całkowitą inną niż -1, zamiast tego zostanie użyta wartość limitu czasu zerowego. (Oznacza to, że metoda zwraca wartość bez oczekiwania, jeśli nie można uzyskać blokady natychmiast). Jeśli określisz wartość TimeSpan , która reprezentuje ujemną liczbę milisekund innych niż -1, ArgumentOutOfRangeException zostanie zgłoszony.
Konstruktory
| Nazwa | Opis |
|---|---|
| ReaderWriterLock() |
Inicjuje nowe wystąpienie klasy ReaderWriterLock. |
Właściwości
| Nazwa | Opis |
|---|---|
| IsReaderLockHeld |
Pobiera wartość wskazującą, czy bieżący wątek przechowuje blokadę czytnika. |
| IsWriterLockHeld |
Pobiera wartość wskazującą, czy bieżący wątek przechowuje blokadę modułu zapisywania. |
| WriterSeqNum |
Pobiera bieżący numer sekwencji. |
Metody
| Nazwa | Opis |
|---|---|
| AcquireReaderLock(Int32) |
Uzyskuje blokadę czytnika przy użyciu Int32 wartości limitu czasu. |
| AcquireReaderLock(TimeSpan) |
Uzyskuje blokadę TimeSpan czytnika przy użyciu wartości limitu czasu. |
| AcquireWriterLock(Int32) |
Uzyskuje blokadę Int32 modułu zapisywania przy użyciu wartości limitu czasu. |
| AcquireWriterLock(TimeSpan) |
Uzyskuje blokadę TimeSpan modułu zapisywania przy użyciu wartości limitu czasu. |
| AnyWritersSince(Int32) |
Wskazuje, czy blokada składnika zapisywania została udzielona dla dowolnego wątku od czasu uzyskania numeru sekwencji. |
| DowngradeFromWriterLock(LockCookie) |
Przywraca stan blokady wątku do tego, co było wcześniej UpgradeToWriterLock(Int32) wywoływane. |
| Equals(Object) |
Określa, czy określony obiekt jest równy bieżącemu obiektowi. (Odziedziczone po Object) |
| Finalize() |
Gwarantuje, że zasoby są zwalniane, a inne operacje oczyszczania są wykonywane, gdy moduł odśmiecający elementy bezużyteczne ReaderWriterLock odzyska obiekt. |
| GetHashCode() |
Służy jako domyślna funkcja skrótu. (Odziedziczone po Object) |
| GetType() |
Pobiera Type bieżącego wystąpienia. (Odziedziczone po Object) |
| MemberwiseClone() |
Tworzy płytkią kopię bieżącego Object. (Odziedziczone po Object) |
| ReleaseLock() |
Zwalnia blokadę, niezależnie od liczby prób uzyskania blokady przez wątek. |
| ReleaseReaderLock() |
Dekrementuje liczbę blokad. |
| ReleaseWriterLock() |
Dekrementuje liczbę blokad w blokadzie modułu zapisywania. |
| RestoreLock(LockCookie) |
Przywraca stan blokady wątku do tego, co było przed wywołaniem metody ReleaseLock(). |
| ToString() |
Zwraca ciąg reprezentujący bieżący obiekt. (Odziedziczone po Object) |
| UpgradeToWriterLock(Int32) |
Uaktualnia blokadę czytnika do blokady modułu zapisywania przy użyciu Int32 wartości limitu czasu. |
| UpgradeToWriterLock(TimeSpan) |
Uaktualnia blokadę czytnika do blokady modułu zapisywania przy użyciu |
Dotyczy
Bezpieczeństwo wątkowe
Ten typ jest bezpieczny wątkiem.