ReaderWriterLock Класс
Определение
Важно!
Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
Определяет блокировку, которая поддерживает отдельные записи и несколько средств чтения.
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
- Наследование
- Наследование
-
ReaderWriterLock
- Атрибуты
Примеры
В следующем примере показано, как использовать ReaderWriterLock для защиты общего ресурса целочисленное значение с именем resource, которое одновременно считывается и записывается исключительно несколькими потоками. Обратите внимание, что объявлен на ReaderWriterLock уровне класса, чтобы он был видим для всех потоков.
// 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
Комментарии
Это важно
Платформа .NET Framework имеет две блокировки записи чтения, ReaderWriterLockSlim и ReaderWriterLock. ReaderWriterLockSlim рекомендуется для всех новых разработок. ReaderWriterLockSlim аналогичен ReaderWriterLock, но он имеет упрощенные правила для рекурсии и обновления и понижения состояния блокировки. ReaderWriterLockSlim избегает многих случаев потенциальной взаимоблокировки. Кроме того, производительность ReaderWriterLockSlim значительно выше ReaderWriterLock.
ReaderWriterLock используется для синхронизации доступа к ресурсу. В любой момент он разрешает одновременный доступ на чтение для нескольких потоков или доступ на запись для одного потока. В ситуации, когда ресурс изменяется редко, обеспечивает лучшую пропускную способность, ReaderWriterLock чем простая блокировка за один раз, например Monitor.
ReaderWriterLock лучше всего работает, когда большинство операций доступа являются чтением, в то время как записи нечасто и короткой длительности. Несколько читателей чередуются с одними писателями, чтобы ни читатели, ни писатели не блокировались в течение длительных периодов.
Note
Хранение блокировок чтения или блокировок записи в течение длительного периода приведет к нехватке других потоков. Для повышения производительности рекомендуется перенастроить приложение, чтобы свести к минимуму продолжительность записи.
Поток может содержать блокировку чтения или блокировку записи, но не одновременно. Вместо освобождения блокировки чтения для получения блокировки записи можно использовать UpgradeToWriterLock и DowngradeFromWriterLock.
Рекурсивные запросы на блокировку увеличивают количество блокировок.
Читатели и записи помещаются в очередь отдельно. Когда поток освобождает блокировку записи, все потоки, ожидающие в очереди чтения в этом моменте, предоставляются блокировки чтения; когда все эти блокировки чтения были освобождены, следующий поток, ожидая в очереди записи, если таковые имеются, предоставляется блокировка записи и т. д. Другими словами, ReaderWriterLock чередуются между коллекцией читателей и одним писателем.
Хотя поток в очереди записи ожидает освобождения активных блокировок чтения, потоки, запрашивающие новые блокировки чтения, накапливаются в очереди чтения. Их запросы не предоставляются, даже если они могут совместно использовать одновременный доступ с существующими владельцами блокировки чтения; это помогает защитить писателей от неограниченного блокирования читателями.
Большинство методов получения блокировок для принятия значений ReaderWriterLock времени ожидания. Используйте время ожидания, чтобы избежать взаимоблокировок в приложении. Например, поток может получить блокировку записи для одного ресурса, а затем запросить блокировку чтения для второго ресурса; в то же время другой поток может получить блокировку записи во втором ресурсе и запросить блокировку чтения в первую очередь. Если время ожидания не используется, потоки взаимоблокировки.
Если истекает интервал времени ожидания и запрос блокировки не предоставлен, метод возвращает управление вызывающему потоку путем вызова ApplicationException. Поток может перехватывать это исключение и определять, какие действия следует предпринять далее.
Время ожидания выражается в миллисекундах. Если вы используете System.TimeSpan для указания времени ожидания, используемое значение — общее количество целых миллисекундах, представленных параметром TimeSpan. В следующей таблице показаны допустимые значения времени ожидания в миллисекундах.
| Value | Description |
|---|---|
| -1 | Поток ожидает, пока блокировка не будет получена, независимо от того, сколько времени она занимает. Для методов, определяющих время ожидания целых чисел, можно использовать константу Infinite . |
| 0 | Поток не ожидает получения блокировки. Если блокировка не может быть получена немедленно, метод возвращается. |
| >0 | Количество миллисекунда для ожидания. |
За исключением -1 отрицательные значения времени ожидания не допускаются. Если указать отрицательное целое число, отличное от -1, используется значение времени ожидания нуля. (То есть метод возвращается без ожидания, если блокировка не может быть получена немедленно.) Если указать TimeSpan значение, представляющее отрицательное число миллисекунда, отличное от -1, ArgumentOutOfRangeException создается.
Конструкторы
| Имя | Описание |
|---|---|
| ReaderWriterLock() |
Инициализирует новый экземпляр класса ReaderWriterLock. |
Свойства
| Имя | Описание |
|---|---|
| IsReaderLockHeld |
Возвращает значение, указывающее, содержит ли текущий поток блокировку чтения. |
| IsWriterLockHeld |
Возвращает значение, указывающее, содержит ли текущий поток блокировку записи. |
| WriterSeqNum |
Возвращает текущий номер последовательности. |
Методы
| Имя | Описание |
|---|---|
| AcquireReaderLock(Int32) |
Получает блокировку Int32 чтения, используя значение времени ожидания. |
| AcquireReaderLock(TimeSpan) |
Получает блокировку чтения, используя TimeSpan значение времени ожидания. |
| AcquireWriterLock(Int32) |
Получает блокировку Int32 записи, используя значение времени ожидания. |
| AcquireWriterLock(TimeSpan) |
Получает блокировку TimeSpan записи, используя значение времени ожидания. |
| AnyWritersSince(Int32) |
Указывает, предоставлена ли блокировка записи любому потоку после получения номера последовательности. |
| DowngradeFromWriterLock(LockCookie) |
Восстанавливает состояние блокировки потока до UpgradeToWriterLock(Int32) вызова. |
| Equals(Object) |
Определяет, равен ли указанный объект текущему объекту. (Унаследовано от Object) |
| Finalize() |
Гарантирует, что ресурсы освобождены и другие операции очистки выполняются при освобождении ReaderWriterLock объекта сборщиком мусора. |
| GetHashCode() |
Служит хэш-функцией по умолчанию. (Унаследовано от Object) |
| GetType() |
Возвращает Type текущего экземпляра. (Унаследовано от Object) |
| MemberwiseClone() |
Создает неглубокую копию текущей Object. (Унаследовано от Object) |
| ReleaseLock() |
Освобождает блокировку независимо от количества операций, когда поток получил блокировку. |
| ReleaseReaderLock() |
Уменьшает на единицу счетчик блокировок. |
| ReleaseWriterLock() |
Уменьшает количество блокировок на блокировке записи. |
| RestoreLock(LockCookie) |
Восстанавливает состояние блокировки потока до ReleaseLock()вызова. |
| ToString() |
Возвращает строку, представляющую текущий объект. (Унаследовано от Object) |
| UpgradeToWriterLock(Int32) |
Обновляет блокировку чтения до блокировки записи, используя Int32 значение времени ожидания. |
| UpgradeToWriterLock(TimeSpan) |
Обновляет блокировку чтения до блокировки записи, используя |
Применяется к
Потокобезопасность
Этот тип является потокобезопасной.