ReaderWriterLock 类
定义
重要
一些信息与预发行产品相关,相应产品在发行之前可能会进行重大修改。 对于此处提供的信息,Microsoft 不作任何明示或暗示的担保。
定义支持单个编写器和多个读取器的锁。
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
- 属性
示例
以下示例演示如何使用 a 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来返回对调用线程的控制。 线程可以捕获此异常,并确定下一步要执行的操作。
超时以毫秒为单位表示。 如果使用 a
| 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) |
使用 |
适用于
线程安全性
此类型是线程安全的。