ReaderWriterLockSlim 类

定义

表示用于管理资源访问的锁定状态,可实现多线程读取或进行独占式写入访问。Represents a lock that is used to manage access to a resource, allowing multiple threads for reading or exclusive access for writing.

public ref class ReaderWriterLockSlim : IDisposable
public class ReaderWriterLockSlim : IDisposable
type ReaderWriterLockSlim = class
    interface IDisposable
Public Class ReaderWriterLockSlim
Implements IDisposable
继承
ReaderWriterLockSlim
实现

示例

下面的示例演示了一个简单的同步缓存,该缓存包含包含整数键的字符串。The following example shows a simple synchronized cache that holds strings with integer keys. ReaderWriterLockSlim 的实例用于同步对充当内部缓存的 Dictionary<TKey,TValue> 的访问。An instance of ReaderWriterLockSlim is used to synchronize access to the Dictionary<TKey,TValue> that serves as the inner cache.

该示例包括要添加到缓存中、从缓存中删除以及从缓存中读取的简单方法。The example includes simple methods to add to the cache, delete from the cache, and read from the cache. 为了演示超时,此示例包含一个方法,该方法仅在指定的超时时间内添加到缓存中。To demonstrate time-outs, the example includes a method that adds to the cache only if it can do so within a specified time-out.

为了演示可升级模式,该示例包含一个方法,该方法检索与键关联的值,并将其与新值进行比较。To demonstrate upgradeable mode, the example includes a method that retrieves the value associated with a key and compares it with a new value. 如果值不变,则方法将返回一个状态,指示没有任何更改。If the value is unchanged, the method returns a status indicating no change. 如果未找到键的值,则插入键/值对。If no value is found for the key, the key/value pair is inserted. 如果值已更改,则会更新。If the value has changed, it is updated. 可升级模式允许线程从读取访问权限升级到按需写入访问权限,而不会导致死锁风险。Upgradeable mode allows the thread to upgrade from read access to write access as needed, without the risk of deadlocks.

该示例包含一个嵌套枚举,该枚举指定演示可升级模式的方法的返回值。The example includes a nested enumeration that specifies the return values for the method that demonstrates upgradeable mode.

该示例使用无参数构造函数创建锁,因此不允许使用递归。The example uses the parameterless constructor to create the lock, so recursion is not allowed. 当锁定不允许递归时,对 ReaderWriterLockSlim 进行编程更简单,并且不易出错。Programming the ReaderWriterLockSlim is simpler and less prone to error when the lock does not allow recursion.

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks

public class SynchronizedCache 
{
    private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
    private Dictionary<int, string> innerCache = new Dictionary<int, string>();

    public int Count
    { get { return innerCache.Count; } }

    public string Read(int key)
    {
        cacheLock.EnterReadLock();
        try
        {
            return innerCache[key];
        }
        finally
        {
            cacheLock.ExitReadLock();
        }
    }

    public void Add(int key, string value)
    {
        cacheLock.EnterWriteLock();
        try
        {
            innerCache.Add(key, value);
        }
        finally
        {
            cacheLock.ExitWriteLock();
        }
    }

    public bool AddWithTimeout(int key, string value, int timeout)
    {
        if (cacheLock.TryEnterWriteLock(timeout))
        {
            try
            {
                innerCache.Add(key, value);
            }
            finally
            {
                cacheLock.ExitWriteLock();
            }
            return true;
        }
        else
        {
            return false;
        }
    }

    public AddOrUpdateStatus AddOrUpdate(int key, string value)
    {
        cacheLock.EnterUpgradeableReadLock();
        try
        {
            string result = null;
            if (innerCache.TryGetValue(key, out result))
            {
                if (result == value)
                {
                    return AddOrUpdateStatus.Unchanged;
                }
                else
                {
                    cacheLock.EnterWriteLock();
                    try
                    {
                        innerCache[key] = value;
                    }
                    finally
                    {
                        cacheLock.ExitWriteLock();
                    }
                    return AddOrUpdateStatus.Updated;
                }
            }
            else
            {
                cacheLock.EnterWriteLock();
                try
                {
                    innerCache.Add(key, value);
                }
                finally
                {
                    cacheLock.ExitWriteLock();
                }
                return AddOrUpdateStatus.Added;
            }
        }
        finally
        {
            cacheLock.ExitUpgradeableReadLock();
        }
    }

    public void Delete(int key)
    {
        cacheLock.EnterWriteLock();
        try
        {
            innerCache.Remove(key);
        }
        finally
        {
            cacheLock.ExitWriteLock();
        }
    }

    public enum AddOrUpdateStatus
    {
        Added,
        Updated,
        Unchanged
    };

    ~SynchronizedCache()
    {
       if (cacheLock != null) cacheLock.Dispose();
    }
}
Public Class SynchronizedCache
    Private cacheLock As New ReaderWriterLockSlim()
    Private innerCache As New Dictionary(Of Integer, String)

    Public ReadOnly Property Count As Integer
       Get
          Return innerCache.Count
       End Get
    End Property
    
    Public Function Read(ByVal key As Integer) As String
        cacheLock.EnterReadLock()
        Try
            Return innerCache(key)
        Finally
            cacheLock.ExitReadLock()
        End Try
    End Function

    Public Sub Add(ByVal key As Integer, ByVal value As String)
        cacheLock.EnterWriteLock()
        Try
            innerCache.Add(key, value)
        Finally
            cacheLock.ExitWriteLock()
        End Try
    End Sub

    Public Function AddWithTimeout(ByVal key As Integer, ByVal value As String, _
                                   ByVal timeout As Integer) As Boolean
        If cacheLock.TryEnterWriteLock(timeout) Then
            Try
                innerCache.Add(key, value)
            Finally
                cacheLock.ExitWriteLock()
            End Try
            Return True
        Else
            Return False
        End If
    End Function

    Public Function AddOrUpdate(ByVal key As Integer, _
                                ByVal value As String) As AddOrUpdateStatus
        cacheLock.EnterUpgradeableReadLock()
        Try
            Dim result As String = Nothing
            If innerCache.TryGetValue(key, result) Then
                If result = value Then
                    Return AddOrUpdateStatus.Unchanged
                Else
                    cacheLock.EnterWriteLock()
                    Try
                        innerCache.Item(key) = value
                    Finally
                        cacheLock.ExitWriteLock()
                    End Try
                    Return AddOrUpdateStatus.Updated
                End If
            Else
                cacheLock.EnterWriteLock()
                Try
                    innerCache.Add(key, value)
                Finally
                    cacheLock.ExitWriteLock()
                End Try
                Return AddOrUpdateStatus.Added
            End If
        Finally
            cacheLock.ExitUpgradeableReadLock()
        End Try
    End Function

    Public Sub Delete(ByVal key As Integer)
        cacheLock.EnterWriteLock()
        Try
            innerCache.Remove(key)
        Finally
            cacheLock.ExitWriteLock()
        End Try
    End Sub

    Public Enum AddOrUpdateStatus
        Added
        Updated
        Unchanged
    End Enum

    Protected Overrides Sub Finalize()
       If cacheLock IsNot Nothing Then cacheLock.Dispose()
    End Sub
End Class

然后,下面的代码使用 SynchronizedCache 对象存储蔬菜名称的字典。The following code then uses the SynchronizedCache object to store a dictionary of vegetable names. 它创建三个任务。It creates three tasks. 首先将存储在数组中的蔬菜名称写入 SynchronizedCache 实例。The first writes the names of vegetables stored in an array to a SynchronizedCache instance. 第二个和第三个任务以升序顺序显示了蔬菜的名称(从低索引到高索引)。The second and third task display the names of the vegetables, the first in ascending order (from low index to high index), the second in descending order. 最终任务会搜索字符串 "cucumber",找到它时,将调用 EnterUpgradeableReadLock 方法来替换字符串 "绿色 bean"。The final task searches for the string "cucumber" and, when it finds it, calls the EnterUpgradeableReadLock method to substitute the string "green bean".

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks

public class Example
{
   public static void Main()
   {
      var sc = new SynchronizedCache();
      var tasks = new List<Task>();
      int itemsWritten = 0;

      // Execute a writer.
      tasks.Add(Task.Run( () => { String[] vegetables = { "broccoli", "cauliflower",
                                                          "carrot", "sorrel", "baby turnip",
                                                          "beet", "brussel sprout",
                                                          "cabbage", "plantain",
                                                          "spinach", "grape leaves",
                                                          "lime leaves", "corn",
                                                          "radish", "cucumber",
                                                          "raddichio", "lima beans" };
                                  for (int ctr = 1; ctr <= vegetables.Length; ctr++)
                                     sc.Add(ctr, vegetables[ctr - 1]);

                                  itemsWritten = vegetables.Length;
                                  Console.WriteLine("Task {0} wrote {1} items\n",
                                                    Task.CurrentId, itemsWritten);
                                } ));
      // Execute two readers, one to read from first to last and the second from last to first.
      for (int ctr = 0; ctr <= 1; ctr++) {
         bool desc = Convert.ToBoolean(ctr);
         tasks.Add(Task.Run( () => { int start, last, step;
                                     int items;
                                     do {
                                        String output = String.Empty;
                                        items = sc.Count;
                                        if (! desc) {
                                           start = 1;
                                           step = 1;
                                           last = items;
                                        }
                                        else {
                                           start = items;
                                           step = -1;
                                           last = 1;
                                        }

                                        for (int index = start; desc ? index >= last : index <= last; index += step)
                                           output += String.Format("[{0}] ", sc.Read(index));

                                        Console.WriteLine("Task {0} read {1} items: {2}\n",
                                                          Task.CurrentId, items, output);
                                     } while (items < itemsWritten | itemsWritten == 0);
                             } ));
      }
      // Execute a red/update task.
      tasks.Add(Task.Run( () => { Thread.Sleep(100);
                                  for (int ctr = 1; ctr <= sc.Count; ctr++) {
                                     String value = sc.Read(ctr);
                                     if (value == "cucumber")
                                        if (sc.AddOrUpdate(ctr, "green bean") != SynchronizedCache.AddOrUpdateStatus.Unchanged)
                                           Console.WriteLine("Changed 'cucumber' to 'green bean'");
                                  }
                                } ));

      // Wait for all three tasks to complete.
      Task.WaitAll(tasks.ToArray());

      // Display the final contents of the cache.
      Console.WriteLine();
      Console.WriteLine("Values in synchronized cache: ");
      for (int ctr = 1; ctr <= sc.Count; ctr++)
         Console.WriteLine("   {0}: {1}", ctr, sc.Read(ctr));
   }
}
// The example displays the following output:
//    Task 1 read 0 items:
//
//    Task 3 wrote 17 items
//
//
//    Task 1 read 17 items: [broccoli] [cauliflower] [carrot] [sorrel] [baby turnip] [
//    beet] [brussel sprout] [cabbage] [plantain] [spinach] [grape leaves] [lime leave
//    s] [corn] [radish] [cucumber] [raddichio] [lima beans]
//
//    Task 2 read 0 items:
//
//    Task 2 read 17 items: [lima beans] [raddichio] [cucumber] [radish] [corn] [lime
//    leaves] [grape leaves] [spinach] [plantain] [cabbage] [brussel sprout] [beet] [b
//    aby turnip] [sorrel] [carrot] [cauliflower] [broccoli]
//
//    Changed 'cucumber' to 'green bean'
//
//    Values in synchronized cache:
//       1: broccoli
//       2: cauliflower
//       3: carrot
//       4: sorrel
//       5: baby turnip
//       6: beet
//       7: brussel sprout
//       8: cabbage
//       9: plantain
//       10: spinach
//       11: grape leaves
//       12: lime leaves
//       13: corn
//       14: radish
//       15: green bean
//       16: raddichio
//       17: lima beans
Public Module Example
   Public Sub Main()
      Dim sc As New SynchronizedCache()
      Dim tasks As New List(Of Task)
      Dim itemsWritten As Integer
      
      ' Execute a writer.
      tasks.Add(Task.Run( Sub()
                             Dim vegetables() As String = { "broccoli", "cauliflower",
                                                            "carrot", "sorrel", "baby turnip",
                                                            "beet", "brussel sprout",
                                                            "cabbage", "plantain",
                                                            "spinach", "grape leaves",
                                                            "lime leaves", "corn",
                                                            "radish", "cucumber",
                                                            "raddichio", "lima beans" }
                             For ctr As Integer = 1 to vegetables.Length
                                sc.Add(ctr, vegetables(ctr - 1))
                             Next
                             itemsWritten = vegetables.Length
                             Console.WriteLine("Task {0} wrote {1} items{2}",
                                               Task.CurrentId, itemsWritten, vbCrLf)
                          End Sub))
      ' Execute two readers, one to read from first to last and the second from last to first.
      For ctr As Integer = 0 To 1
         Dim flag As Integer = ctr
         tasks.Add(Task.Run( Sub()
                                Dim start, last, stp As Integer
                                Dim items As Integer
                                Do
                                   Dim output As String = String.Empty
                                   items = sc.Count
                                   If flag = 0 Then
                                      start = 1 : stp = 1 : last = items
                                   Else
                                      start = items : stp = -1 : last = 1
                                   End If
                                   For index As Integer = start To last Step stp
                                      output += String.Format("[{0}] ", sc.Read(index))
                                   Next
                                   Console.WriteLine("Task {0} read {1} items: {2}{3}",
                                                           Task.CurrentId, items, output,
                                                           vbCrLf)
                                Loop While items < itemsWritten Or itemsWritten = 0
                             End Sub))
      Next
      ' Execute a red/update task.
      tasks.Add(Task.Run( Sub()
                             For ctr As Integer = 1 To sc.Count
                                Dim value As String = sc.Read(ctr)
                                If value = "cucumber" Then
                                   If sc.AddOrUpdate(ctr, "green bean") <> SynchronizedCache.AddOrUpdateStatus.Unchanged Then
                                      Console.WriteLine("Changed 'cucumber' to 'green bean'")
                                   End If
                                End If
                             Next
                          End Sub ))

      ' Wait for all three tasks to complete.
      Task.WaitAll(tasks.ToArray())

      ' Display the final contents of the cache.
      Console.WriteLine()
      Console.WriteLine("Values in synchronized cache: ")
      For ctr As Integer = 1 To sc.Count
         Console.WriteLine("   {0}: {1}", ctr, sc.Read(ctr))
      Next
   End Sub
End Module
' The example displays output like the following:
'    Task 1 read 0 items:
'
'    Task 3 wrote 17 items
'
'    Task 1 read 17 items: [broccoli] [cauliflower] [carrot] [sorrel] [baby turnip] [
'    beet] [brussel sprout] [cabbage] [plantain] [spinach] [grape leaves] [lime leave
'    s] [corn] [radish] [cucumber] [raddichio] [lima beans]
'
'    Task 2 read 0 items:
'
'    Task 2 read 17 items: [lima beans] [raddichio] [cucumber] [radish] [corn] [lime
'    leaves] [grape leaves] [spinach] [plantain] [cabbage] [brussel sprout] [beet] [b
'    aby turnip] [sorrel] [carrot] [cauliflower] [broccoli]
'
'    Changed 'cucumber' to 'green bean'
'
'    Values in synchronized cache:
'       1: broccoli
'       2: cauliflower
'       3: carrot
'       4: sorrel
'       5: baby turnip
'       6: beet
'       7: brussel sprout
'       8: cabbage
'       9: plantain
'       10: spinach
'       11: grape leaves
'       12: lime leaves
'       13: corn
'       14: radish
'       15: green bean
'       16: raddichio
'       17: lima beans

注解

使用 ReaderWriterLockSlim 保护由多个线程读取的资源并一次写入一个线程。Use ReaderWriterLockSlim to protect a resource that is read by multiple threads and written to by one thread at a time. ReaderWriterLockSlim 允许多个线程处于读取模式,则允许一个线程处于具有独占锁定所有权的写入模式,并且允许具有读取访问权限的一个线程处于可升级读取模式,在该模式下,线程可以升级到写入模式,而无需放弃对资源的读取访问权限。ReaderWriterLockSlim allows multiple threads to be in read mode, allows one thread to be in write mode with exclusive ownership of the lock, and allows one thread that has read access to be in upgradeable read mode, from which the thread can upgrade to write mode without having to relinquish its read access to the resource.

备注

虽然 ReaderWriterLockSlim 类似于 ReaderWriterLock,但不同之处在于,前者简化了递归规则以及锁状态的升级和降级规则。ReaderWriterLockSlim is similar to ReaderWriterLock, but it has simplified rules for recursion and for upgrading and downgrading lock state. ReaderWriterLockSlim 避免了许多潜在的死锁情况。ReaderWriterLockSlim avoids many cases of potential deadlock. 另外,ReaderWriterLockSlim 的性能显著优于 ReaderWriterLockIn addition, the performance of ReaderWriterLockSlim is significantly better than ReaderWriterLock. 建议对所有新开发的项目使用 ReaderWriterLockSlimReaderWriterLockSlim is recommended for all new development.

默认情况下,ReaderWriterLockSlim 的新实例是使用 LockRecursionPolicy.NoRecursion 标志创建的,不允许使用递归。By default, new instances of ReaderWriterLockSlim are created with the LockRecursionPolicy.NoRecursion flag and do not allow recursion. 建议对所有新的开发使用此默认策略,因为递归引入了不必要的复杂性,并使代码更容易发生死锁。This default policy is recommended for all new development, because recursion introduces unnecessary complications and makes your code more prone to deadlocks. 若要简化从使用 MonitorReaderWriterLock的现有项目的迁移,可以使用 LockRecursionPolicy.SupportsRecursion 标志创建允许递归的 ReaderWriterLockSlim 实例。To simplify migration from existing projects that use Monitor or ReaderWriterLock, you can use the LockRecursionPolicy.SupportsRecursion flag to create instances of ReaderWriterLockSlim that allow recursion.

线程可以在三种模式下进入锁定:读取模式、写入模式和可升级读取模式。A thread can enter the lock in three modes: read mode, write mode, and upgradeable read mode. (在本主题的其余部分中,"可升级的读取模式" 称为 "可升级模式",将优先使用 "输入 x 模式" 短语来 "在 x 模式下进入锁定"。)(In the rest of this topic, "upgradeable read mode" is referred to as "upgradeable mode", and the phrase "enter x mode" is used in preference to the longer phrase "enter the lock in x mode".)

不管递归策略如何,在任何时候都只能有一个线程处于写入模式。Regardless of recursion policy, only one thread can be in write mode at any time. 当线程处于写入模式时,任何其他线程都不能在任何模式下进入锁定状态。When a thread is in write mode, no other thread can enter the lock in any mode. 在任何时候,只能有一个线程处于可升级模式。Only one thread can be in upgradeable mode at any time. 任意数量的线程都可以处于读取模式,并且在其他线程处于读取模式时,可以有一个处于可升级模式的线程。Any number of threads can be in read mode, and there can be one thread in upgradeable mode while other threads are in read mode.

重要

此类型实现 IDisposable 接口。This type implements the IDisposable interface. 在使用完类型后,您应直接或间接释放类型。When you have finished using the type, you should dispose of it either directly or indirectly. 若要直接释放类型,请在 Disposetry/ 块中调用其 catch 方法。To dispose of the type directly, call its Dispose method in a try/catch block. 若要间接释放类型,请使用 using(在 C# 中)或 Using(在 Visual Basic 中)等语言构造。To dispose of it indirectly, use a language construct such as using (in C#) or Using (in Visual Basic). 有关详细信息,请参阅 IDisposable 接口主题中的“使用实现 IDisposable 的对象”一节。For more information, see the "Using an Object that Implements IDisposable" section in the IDisposable interface topic.

ReaderWriterLockSlim 具有托管线程关联;也就是说,每个 Thread 对象都必须进行自己的方法调用来进入和退出锁模式。ReaderWriterLockSlim has managed thread affinity; that is, each Thread object must make its own method calls to enter and exit lock modes. 任何线程都无法更改另一个线程的模式。No thread can change the mode of another thread.

如果 ReaderWriterLockSlim 不允许使用递归,尝试进入锁定的线程可能会出于多种原因而阻塞:If a ReaderWriterLockSlim does not allow recursion, a thread that tries to enter the lock can block for several reasons:

  • 如果有等待进入写入模式的线程或在写入模式下有单个线程,则尝试进入读取模式的线程会被阻止。A thread that tries to enter read mode blocks if there are threads waiting to enter write mode or if there is a single thread in write mode.

    备注

    在编写器排队时阻止新的读取器是优先写入器的锁公平策略。Blocking new readers when writers are queued is a lock fairness policy that favors writers. 当前的公平策略在最常见的情况下,将公平与读者和编写者进行平衡,以提高吞吐量。The current fairness policy balances fairness to readers and writers, to promote throughput in the most common scenarios. 未来版本的 .NET Framework.NET Framework 可能会引入新的公平策略。Future versions of the .NET Framework.NET Framework may introduce new fairness policies.

  • 如果已存在处于可升级模式的线程,则为尝试进入可升级模式的线程,如果存在等待进入写入模式的线程,则为; 如果在写入模式下有单个线程,则为。A thread that tries to enter upgradeable mode blocks if there is already a thread in upgradeable mode, if there are threads waiting to enter write mode, or if there is a single thread in write mode.

  • 如果在三种模式中的任何一种模式下有线程,则尝试进入写入模式块的线程。A thread that tries to enter write mode blocks if there is a thread in any of the three modes.

升级和降级锁Upgrading and Downgrading Locks

可升级模式适用于以下情况:线程通常会从受保护的资源中读取内容,但如果满足某些条件,则可能需要对其进行写入。Upgradeable mode is intended for cases where a thread usually reads from the protected resource, but might need to write to it if some condition is met. 在可升级模式下进入 ReaderWriterLockSlim 的线程具有对受保护资源的读取访问权限,并且可以通过调用 EnterWriteLockTryEnterWriteLock 方法升级为写入模式。A thread that has entered a ReaderWriterLockSlim in upgradeable mode has read access to the protected resource, and can upgrade to write mode by calling the EnterWriteLock or TryEnterWriteLock methods. 由于一次只能有一个处于可升级模式的线程,因此当不允许递归时,升级到写入模式不会死锁,这是默认策略。Because there can be only one thread in upgradeable mode at a time, upgrading to write mode cannot deadlock when recursion is not allowed, which is the default policy.

重要

不管递归策略如何,最初进入读取模式的线程都不允许升级到可升级模式或写入模式,因为这种模式产生了死锁的概率。Regardless of recursion policy, a thread that initially entered read mode is not allowed to upgrade to upgradeable mode or write mode, because that pattern creates a strong probability of deadlocks. 例如,在读取模式下的两个线程都尝试进入写入模式时,它们会死锁。For example, if two threads in read mode both try to enter write mode, they will deadlock. 可升级模式旨在避免这类死锁。Upgradeable mode is designed to avoid such deadlocks.

如果其他线程处于读取模式,则正在升级的线程会被阻止。If there are other threads in read mode, the thread that is upgrading blocks. 当线程被阻止时,其他尝试进入读取模式的线程将被阻止。While the thread is blocked, other threads that try to enter read mode are blocked. 如果所有线程都退出了读取模式,则被阻止的可升级线程进入写入模式。When all threads have exited from read mode, the blocked upgradeable thread enters write mode. 如果有其他等待进入写入模式的线程,它们将保持阻止状态,因为处于可升级模式的单个线程会阻止它们获取对资源的独占访问权限。If there are other threads waiting to enter write mode, they remain blocked, because the single thread that is in upgradeable mode prevents them from gaining exclusive access to the resource.

当处于可升级模式的线程退出写入模式时,除非有等待进入写入模式的线程,否则等待进入读取模式的其他线程可以执行此操作。When the thread in upgradeable mode exits write mode, other threads that are waiting to enter read mode can do so, unless there are threads waiting to enter write mode. 处于可升级模式的线程可以无限期地升级和降级,只要它是写入受保护资源的唯一线程。The thread in upgradeable mode can upgrade and downgrade indefinitely, as long as it is the only thread that writes to the protected resource.

重要

如果允许多个线程进入写入模式或可升级模式,则不得允许一个线程独占可升级模式。If you allow multiple threads to enter write mode or upgradeable mode, you must not allow one thread to monopolize upgradeable mode. 否则,尝试直接进入写入模式的线程会无限期阻止,而在被阻止时,其他线程将无法进入读取模式。Otherwise, threads that try to enter write mode directly will be blocked indefinitely, and while they are blocked, other threads will be unable to enter read mode.

处于可升级模式的线程可以通过首先调用 EnterReadLock 方法,然后调用 ExitUpgradeableReadLock 方法,降级到读取模式。A thread in upgradeable mode can downgrade to read mode by first calling the EnterReadLock method and then calling the ExitUpgradeableReadLock method. 所有锁递归策略都允许使用此降级模式,甚至 NoRecursionThis downgrade pattern is allowed for all lock recursion policies, even NoRecursion.

降级到读取模式后,线程无法重新进入可升级模式,除非它已退出读取模式。After downgrading to read mode, a thread cannot reenter upgradeable mode until it has exited from read mode.

以递归方式进入锁定Entering the Lock Recursively

您可以通过使用指定锁定策略的 ReaderWriterLockSlim(LockRecursionPolicy) 构造函数并指定 LockRecursionPolicy.SupportsRecursion来创建支持递归锁定条目的 ReaderWriterLockSlimYou can create a ReaderWriterLockSlim that supports recursive lock entry by using the ReaderWriterLockSlim(LockRecursionPolicy) constructor that specifies lock policy, and specifying LockRecursionPolicy.SupportsRecursion.

备注

建议不要将递归用于新的开发,因为这会引入不必要的复杂性,并使代码更容易发生死锁。The use of recursion is not recommended for new development, because it introduces unnecessary complications and makes your code more prone to deadlocks.

对于允许递归的 ReaderWriterLockSlim,可以通过以下方式了解线程可输入的模式:For a ReaderWriterLockSlim that allows recursion, the following can be said about the modes a thread can enter:

  • 处于读取模式的线程可以以递归方式进入读取模式,但不能进入写入模式或可升级模式。A thread in read mode can enter read mode recursively, but cannot enter write mode or upgradeable mode. 如果尝试执行此操作,则会引发 LockRecursionExceptionIf it tries to do this, a LockRecursionException is thrown. 进入读取模式,然后进入写入模式或可升级模式是一种具有极大的死锁概率的模式,因此不允许这样做。Entering read mode and then entering write mode or upgradeable mode is a pattern with a strong probability of deadlocks, so it is not allowed. 如前文所述,可升级模式适用于需要升级锁定的情况。As discussed earlier, upgradeable mode is provided for cases where it is necessary to upgrade a lock.

  • 处于可升级模式的线程可以进入写入模式和/或读取模式,并且可以递归输入三种模式中的任何一种。A thread in upgradeable mode can enter write mode and/or read mode, and can enter any of the three modes recursively. 但是,如果有其他线程处于读取模式,则尝试进入写入模式块。However, an attempt to enter write mode blocks if there are other threads in read mode.

  • 处于写入模式的线程可以进入读取模式和/或可升级模式,并且可以递归输入三种模式中的任何一种。A thread in write mode can enter read mode and/or upgradeable mode, and can enter any of the three modes recursively.

  • 未进入锁定状态的线程可以进入任何模式。A thread that has not entered the lock can enter any mode. 尝试输入非递归锁的原因与此尝试相同。This attempt can block for the same reasons as an attempt to enter a non-recursive lock.

线程可以退出它已经按任意顺序进入的模式,只要它进入该模式时,它的每一种模式就会发生的次数完全相同。A thread can exit the modes it has entered in any order, as long as it exits each mode exactly as many times as it entered that mode. 如果线程尝试退出模式的次数过多,或退出未输入的模式,则会引发 SynchronizationLockExceptionIf a thread tries to exit a mode too many times, or to exit a mode it has not entered, a SynchronizationLockException is thrown.

锁定状态Lock States

你可能会发现,将锁视为其状态是非常有用的。You may find it useful to think of the lock in terms of its states. ReaderWriterLockSlim 可以是以下四种状态之一:未输入、读取、升级和写入。A ReaderWriterLockSlim can be in one of four states: not entered, read, upgrade, and write.

  • 未输入:在此状态下,没有线程进入锁定状态(或所有线程都已退出锁)。Not entered: In this state, no threads have entered the lock (or all threads have exited the lock).

  • 读取:在此状态中,一个或多个线程已进入对受保护资源的读取访问权限。Read: In this state, one or more threads have entered the lock for read access to the protected resource.

    备注

    线程可以使用 EnterReadLockTryEnterReadLock 方法进入读取模式锁定状态,也可以通过从可升级模式降级进入。A thread can enter the lock in read mode by using the EnterReadLock or TryEnterReadLock methods, or by downgrading from upgradeable mode.

  • 升级:在此状态下,一个线程已进入具有 "升级到写入访问权限" 选项(即在可升级模式下)的 "读取" 访问锁定,并且零个或多个线程已进入读取访问锁定状态。Upgrade: In this state, one thread has entered the lock for read access with the option to upgrade to write access (that is, in upgradeable mode), and zero or more threads have entered the lock for read access. 一次不能有多个线程可使用升级选项输入锁定;阻止尝试进入可升级模式的其他线程。No more than one thread at a time can enter the lock with the option to upgrade; additional threads that try to enter upgradeable mode are blocked.

  • 写入:在此状态下,一个线程已进入对受保护资源的写访问锁定。Write: In this state, one thread has entered the lock for write access to the protected resource. 该线程独占持有锁。That thread has exclusive possession of the lock. 会阻止任何其他尝试输入锁定的其他线程。Any other thread that tries to enter the lock for any reason is blocked.

下表描述了在线程 t 采用最左侧的列中所述的操作时,锁定状态与不允许递归的锁之间的转换。The following table describes the transitions between lock states, for locks that do not allow recursion, when a thread t takes the action described in the leftmost column. 执行操作时,t 没有模式。At the time it takes the action, t has no mode. (表脚注中介绍了 t 处于可升级模式的特殊情况。)最上面一行介绍锁定的开始状态。(The special case where t is in upgradeable mode is described in the table footnotes.) The top row describes the starting state of the lock. 单元格描述对线程发生的操作,并在括号中显示锁定状态的更改。The cells describe what happens to the thread, and show changes to the lock state in parentheses.

未输入(N)Not entered (N) 读取(R)Read (R) 升级(U)Upgrade (U) 写入(W)Write (W)
t 进入读取模式t enters read mode t 进入(R)。t enters (R). 如果线程正在等待写入模式,则 t 块;否则,t 进入。t blocks if threads are waiting for write mode; otherwise, t enters. 如果线程正在等待写入模式,则 t 块;否则,t 进入。1t blocks if threads are waiting for write mode; otherwise, t enters.1 t 块。t blocks.
t 进入可升级模式t enters upgradeable mode t 进入(U)。t enters (U). 如果线程正在等待写入模式或升级模式,则 t 块;否则,t 进入(U)。t blocks if threads are waiting for write mode or upgrade mode; otherwise, t enters (U). t 块。t blocks. t 块。t blocks.
t 进入写入模式t enters write mode t 进入(W)。t enters (W). t 块。t blocks. t 块。2t blocks.2 t 块。t blocks.

如果 t 在可升级模式下启动,则为1 ; 否则进入读取模式。1 If t starts out in upgradeable mode, it enters read mode. 此操作永远不会阻止。This action never blocks. 锁定状态不会更改。The lock state does not change. (然后,线程可以通过退出可升级模式来完成降级到读取模式。)(The thread can then complete a downgrade to read mode by exiting upgradeable mode.)

2如果 t 在可升级模式下启动,则会阻止处于读取模式的线程。2 If t starts out in upgradeable mode, it blocks if there are threads in read mode. 否则,会升级到写入模式。Otherwise it upgrades to write mode. 锁定状态将更改为写入(W)。The lock state changes to Write (W). 如果 t 块是因为在读取模式下有线程,则当最后一个线程退出读取模式(即使有等待进入写入模式的线程)时,它将进入写入模式。If t blocks because there are threads in read mode, it enters write mode as soon as the last thread exits read mode, even if there are threads waiting to enter write mode.

当由于线程退出锁而发生状态更改时,将选择下一个要唤醒的线程,如下所示:When a state change occurs because a thread exits the lock, the next thread to be awakened is selected as follows:

  • 首先,线程正在等待写入模式,并且已经处于可升级模式(最多可以有一个此类线程)。First, a thread that is waiting for write mode and is already in upgradeable mode (there can be at most one such thread).

  • 如果失败,则为等待写入模式的线程。Failing that, a thread that is waiting for write mode.

  • 如果失败,则为等待可升级模式的线程。Failing that, a thread that is waiting for upgradeable mode.

  • 如果未成功,则为等待读取模式的所有线程。Failing that, all threads that are waiting for read mode.

在前两种情况下,该锁的后续状态始终为 Write (W),在第三种情况下为 Upgrade (U)。The subsequent state of the lock is always Write (W) in the first two cases and Upgrade (U) in the third case, regardless of the state of the lock when the exiting thread triggered the state change. 在最后一种情况下,如果状态更改后有一个处于可升级模式的线程处于可升级模式,则锁定的状态为 "升级(U)"; 否则为 "读取(R)"。In the last case, the state of the lock is Upgrade (U) if there is a thread in upgradeable mode after the state change, and Read (R) otherwise, regardless of the prior state.

构造函数

ReaderWriterLockSlim()

使用默认属性值初始化 ReaderWriterLockSlim 类的新实例。Initializes a new instance of the ReaderWriterLockSlim class with default property values.

ReaderWriterLockSlim(LockRecursionPolicy)

在指定锁定递归策略的情况下初始化 ReaderWriterLockSlim 类的新实例。Initializes a new instance of the ReaderWriterLockSlim class, specifying the lock recursion policy.

属性

CurrentReadCount

获取已进入读取模式锁定状态的独有线程的总数。Gets the total number of unique threads that have entered the lock in read mode.

IsReadLockHeld

获取一个值,该值指示当前线程是否已进入读取模式的锁定状态。Gets a value that indicates whether the current thread has entered the lock in read mode.

IsUpgradeableReadLockHeld

获取一个值,该值指示当前线程是否已进入可升级模式的锁定状态。Gets a value that indicates whether the current thread has entered the lock in upgradeable mode.

IsWriteLockHeld

获取一个值,该值指示当前线程是否已进入写入模式的锁定状态。Gets a value that indicates whether the current thread has entered the lock in write mode.

RecursionPolicy

获取一个值,该值指示当前 ReaderWriterLockSlim 对象的递归策略。Gets a value that indicates the recursion policy for the current ReaderWriterLockSlim object.

RecursiveReadCount

获取当前线程进入读取模式锁定状态的次数,用于指示递归。Gets the number of times the current thread has entered the lock in read mode, as an indication of recursion.

RecursiveUpgradeCount

获取当前线程进入可升级模式锁定状态的次数,用于指示递归。Gets the number of times the current thread has entered the lock in upgradeable mode, as an indication of recursion.

RecursiveWriteCount

获取当前线程进入写入模式锁定状态的次数,用于指示递归。Gets the number of times the current thread has entered the lock in write mode, as an indication of recursion.

WaitingReadCount

获取等待进入读取模式锁定状态的线程总数。Gets the total number of threads that are waiting to enter the lock in read mode.

WaitingUpgradeCount

获取等待进入可升级模式锁定状态的线程总数。Gets the total number of threads that are waiting to enter the lock in upgradeable mode.

WaitingWriteCount

获取等待进入写入模式锁定状态的线程总数。Gets the total number of threads that are waiting to enter the lock in write mode.

方法

Dispose()

释放由 ReaderWriterLockSlim 类的当前实例占用的所有资源。Releases all resources used by the current instance of the ReaderWriterLockSlim class.

EnterReadLock()

尝试进入读取模式锁定状态。Tries to enter the lock in read mode.

EnterUpgradeableReadLock()

尝试进入可升级模式锁定状态。Tries to enter the lock in upgradeable mode.

EnterWriteLock()

尝试进入写入模式锁定状态。Tries to enter the lock in write mode.

Equals(Object)

确定指定的对象是否等于当前对象。Determines whether the specified object is equal to the current object.

(继承自 Object)
ExitReadLock()

减少读取模式的递归计数,并在生成的计数为 0(零)时退出读取模式。Reduces the recursion count for read mode, and exits read mode if the resulting count is 0 (zero).

ExitUpgradeableReadLock()

减少可升级模式的递归计数,并在生成的计数为 0(零)时退出可升级模式。Reduces the recursion count for upgradeable mode, and exits upgradeable mode if the resulting count is 0 (zero).

ExitWriteLock()

减少写入模式的递归计数,并在生成的计数为 0(零)时退出写入模式。Reduces the recursion count for write mode, and exits write mode if the resulting count is 0 (zero).

GetHashCode()

作为默认哈希函数。Serves as the default hash function.

(继承自 Object)
GetType()

获取当前实例的 TypeGets the Type of the current instance.

(继承自 Object)
MemberwiseClone()

创建当前 Object 的浅表副本。Creates a shallow copy of the current Object.

(继承自 Object)
ToString()

返回表示当前对象的字符串。Returns a string that represents the current object.

(继承自 Object)
TryEnterReadLock(Int32)

尝试进入读取模式锁定状态,可以选择整数超时时间。Tries to enter the lock in read mode, with an optional integer time-out.

TryEnterReadLock(TimeSpan)

尝试进入读取模式锁定状态,可以选择超时时间。Tries to enter the lock in read mode, with an optional time-out.

TryEnterUpgradeableReadLock(Int32)

尝试进入可升级模式锁定状态,可以选择超时时间。Tries to enter the lock in upgradeable mode, with an optional time-out.

TryEnterUpgradeableReadLock(TimeSpan)

尝试进入可升级模式锁定状态,可以选择超时时间。Tries to enter the lock in upgradeable mode, with an optional time-out.

TryEnterWriteLock(Int32)

尝试进入写入模式锁定状态,可以选择超时时间。Tries to enter the lock in write mode, with an optional time-out.

TryEnterWriteLock(TimeSpan)

尝试进入写入模式锁定状态,可以选择超时时间。Tries to enter the lock in write mode, with an optional time-out.

适用于

线程安全性

此类型是线程安全的。This type is thread safe.