ReaderWriterLock Třída
Definice
Důležité
Některé informace platí pro předběžně vydaný produkt, který se může zásadně změnit, než ho výrobce nebo autor vydá. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.
Definuje zámek, který podporuje jednotlivé zapisovače a více čtenářů.
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
- Dědičnost
- Dědičnost
-
ReaderWriterLock
- Atributy
Příklady
Následující příklad ukazuje, jak použít ReaderWriterLock k ochraně sdíleného prostředku, celočíselnou hodnotu s názvem resource, která je přečtena souběžně a zapsána výhradně více vlákny. Všimněte si, že ReaderWriterLock je deklarována na úrovni třídy, aby byla viditelná pro všechna vlákna.
// 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
Poznámky
Důležité
Architektura .NET má dva zámky pro čtení, ReaderWriterLockSlim a ReaderWriterLock. ReaderWriterLockSlim se doporučuje pro veškerý nový vývoj. ReaderWriterLockSlim je podobný ReaderWriterLock, ale má zjednodušená pravidla pro rekurze a pro upgrade a downgrading stavu uzamčení. ReaderWriterLockSlim zabraňuje mnoha případům potenciálního zablokování. Kromě toho je výkon ReaderWriterLockSlim výrazně lepší než ReaderWriterLock.
ReaderWriterLock slouží k synchronizaci přístupu k prostředku. V každém okamžiku umožňuje souběžný přístup ke čtení pro více vláken nebo přístup k zápisu pro jedno vlákno. V situaci, kdy se prostředek mění zřídka, ReaderWriterLock poskytuje lepší propustnost než jednoduchý jednorázový zámek, například Monitor.
ReaderWriterLock funguje nejlépe tam, kde je většina přístupů čtení, zatímco zápisy jsou občasné a krátké doby trvání. Více čtenářů se střídá s jedním zapisovačem, takže čtenáři ani autoři nejsou blokovaní po dlouhou dobu.
Note
Podržení zámků čtečky nebo zámků zapisovače po dlouhou dobu bude hladovět další vlákna. Pokud chcete dosáhnout nejlepšího výkonu, zvažte restrukturalizaci aplikace, aby se minimalizovala doba trvání zápisů.
Vlákno může obsahovat zámek čtečky nebo zámek zapisovače, ale ne najednou. Místo uvolnění zámku čtečky za účelem získání zámku zapisovače můžete použít UpgradeToWriterLock a DowngradeFromWriterLock.
Rekurzivní požadavky na uzamčení zvyšují počet zámků.
Čtenáři a zapisovače se zařadí do fronty samostatně. Když vlákno uvolní zámek zapisovače, všechna vlákna čekající ve frontě čtenáře v daném okamžiku jsou uděleny zámky čtenáře; jakmile jsou všechny tyto zámky čtečky uvolněny, další vlákno čekající ve frontě zapisovače, pokud existuje, je udělen zámek zapisovače atd. Jinými slovy, ReaderWriterLock střídá se mezi kolekcí čtenářů a jedním spisovatelem.
Zatímco vlákno ve frontě zapisovače čeká na uvolnění aktivních zámků čtečky, vlákna požadující nové zámky čtečky se hromadí ve frontě čtenáře. Jejich žádosti nejsou uděleny, i když by mohly sdílet souběžný přístup se stávajícími držiteli zámku čtenáře; to pomáhá chránit spisovatele před neomezenou blokování čtenáři.
Většina metod pro získání zámků u hodnot časového limitu ReaderWriterLock pro přijetí. Časové limity použijte, abyste se vyhnuli zablokování ve vaší aplikaci. Vlákno může například získat zámek zapisovače pro jeden prostředek a pak požádat čtenáře o zámek druhého prostředku; Mezitím může další vlákno získat zámek zapisovače u druhého prostředku a požádat o zámek čtenáře na prvním. Pokud nejsou použity časové limity, vlákna zablokování.
Pokud vyprší časový limit a požadavek na zámek nebyl udělen, metoda vrátí řízení volající vlákno vyvoláním ApplicationException. Vlákno může tuto výjimku zachytit a určit, jaká akce se má provést dále.
Časové limity jsou vyjádřeny v milisekundách. Pokud k určení časového limitu použijete System.TimeSpan hodnotu, použije se celkový počet celých milisekund reprezentovaných hodnotou TimeSpan. Následující tabulka uvádí platné hodnoty časového limitu v milisekundách.
| Value | Description |
|---|---|
| -1 | Vlákno čeká na získání zámku bez ohledu na to, jak dlouho trvá. Pro metody, které určují vypršení časového limitu celého čísla, lze použít konstantu Infinite . |
| 0 | Vlákno nečeká na získání zámku. Pokud zámek nelze získat okamžitě, vrátí metoda. |
| >0 | Počet milisekund, které se mají počkat. |
S výjimkou hodnoty -1 nejsou povoleny záporné hodnoty časového limitu. Pokud zadáte jiné záporné celé číslo než -1, použije se místo toho hodnota časového limitu nuly. (To znamená, že metoda vrátí bez čekání, pokud zámek nelze získat okamžitě.) Pokud zadáte TimeSpan hodnotu, která představuje záporný počet milisekund než -1, ArgumentOutOfRangeException je vyvolána.
Konstruktory
| Name | Description |
|---|---|
| ReaderWriterLock() |
Inicializuje novou instanci ReaderWriterLock třídy. |
Vlastnosti
| Name | Description |
|---|---|
| IsReaderLockHeld |
Získá hodnotu určující, zda aktuální vlákno obsahuje zámek čtečky. |
| IsWriterLockHeld |
Získá hodnotu určující, zda aktuální vlákno obsahuje zapisovací zámek. |
| WriterSeqNum |
Získá aktuální pořadové číslo. |
Metody
| Name | Description |
|---|---|
| AcquireReaderLock(Int32) |
Získá zámek čtečky s použitím Int32 hodnoty pro časový limit. |
| AcquireReaderLock(TimeSpan) |
Získá zámek čtečky s použitím TimeSpan hodnoty pro časový limit. |
| AcquireWriterLock(Int32) |
Získá zámek zapisovače pomocí Int32 hodnoty pro časový limit. |
| AcquireWriterLock(TimeSpan) |
Získá zámek zapisovače pomocí TimeSpan hodnoty pro časový limit. |
| AnyWritersSince(Int32) |
Určuje, zda zámek zapisovače byl udělen libovolnému vláknu od získání pořadového čísla. |
| DowngradeFromWriterLock(LockCookie) |
Obnoví stav zámku vlákna na to, co bylo dříve UpgradeToWriterLock(Int32) volána. |
| Equals(Object) |
Určuje, zda je zadaný objekt roven aktuálnímu objektu. (Zděděno od Object) |
| Finalize() |
Zajišťuje uvolnění prostředků a provádění dalších operací čištění, když uvolňování paměti objekt uvolní ReaderWriterLock . |
| GetHashCode() |
Slouží jako výchozí funkce hash. (Zděděno od Object) |
| GetType() |
Získá Type aktuální instance. (Zděděno od Object) |
| MemberwiseClone() |
Vytvoří mělkou kopii aktuálního Object. (Zděděno od Object) |
| ReleaseLock() |
Uvolní zámek bez ohledu na počet, kolikrát vlákno získalo zámek. |
| ReleaseReaderLock() |
Sníží počet zámků. |
| ReleaseWriterLock() |
Sníží počet zámků na zámek zapisovače. |
| RestoreLock(LockCookie) |
Obnoví stav zámku vlákna na to, co bylo před voláním ReleaseLock(). |
| ToString() |
Vrátí řetězec, který představuje aktuální objekt. (Zděděno od Object) |
| UpgradeToWriterLock(Int32) |
Upgraduje zámek čtečky na zámek zapisovače pomocí Int32 hodnoty pro časový limit. |
| UpgradeToWriterLock(TimeSpan) |
Upgraduje zámek čtečky na zámek zapisovače pomocí |
Platí pro
Bezpečný přístup z více vláken
Tento typ je bezpečný pro přístup z více vláken.