地址:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.readerwriterlockslim?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev16.query%3FappId%3DDev16IDEF1%26l%3DZH-CN%26k%3Dk(System.Threading.ReaderWriterLockSlim);k(DevLang-csharp)%26rd%3Dtrue&view=netframework-4.8
标题:ReaderWriterLockSlim 类
文章内容摘抄自微软文档。
表示用于管理资源访问的锁定状态,可实现多线程读取或进行独占式写入访问。
下面的示例演示了一个简单的同步缓存,该缓存包含包含整数键的字符串。 ReaderWriterLockSlim 的实例用于同步对充当内部缓存的 Dictionary<TKey,TValue> 的访问。
该示例包括要添加到缓存中、从缓存中删除以及从缓存中读取的简单方法。 为了演示超时,此示例包含一个方法,该方法仅在指定的超时时间内添加到缓存中。
为了演示可升级模式,该示例包含一个方法,该方法检索与键关联的值,并将其与新值进行比较。 如果值不变,则方法将返回一个状态,指示没有任何更改。 如果未找到键的值,则插入键/值对。 如果值已更改,则会更新。 可升级模式允许线程从读取访问权限升级到按需写入访问权限,而不会导致死锁风险。
该示例包含一个嵌套枚举,该枚举指定演示可升级模式的方法的返回值。
该示例使用无参数构造函数创建锁,因此不允许使用递归。 当锁定不允许递归时,对 ReaderWriterLockSlim 进行编程更简单,并且不易出错。
using System; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic;
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(); } }
注解
使用 ReaderWriterLockSlim 保护由多个线程读取的资源并一次写入一个线程。 ReaderWriterLockSlim 允许多个线程处于读取模式,则允许一个线程处于具有独占锁定所有权的写入模式,并且允许具有读取访问权限的一个线程处于可升级读取模式,在该模式下,线程可以升级到写入模式,而无需放弃对资源的读取访问权限。
备注
虽然 ReaderWriterLockSlim 类似于 ReaderWriterLock,但不同之处在于,前者简化了递归规则以及锁状态的升级和降级规则。 ReaderWriterLockSlim 避免了许多潜在的死锁情况。 另外,ReaderWriterLockSlim 的性能显著优于 ReaderWriterLock。 建议对所有新开发的项目使用 ReaderWriterLockSlim。
默认情况下,ReaderWriterLockSlim 的新实例是使用 LockRecursionPolicy.NoRecursion 标志创建的,不允许使用递归。 建议对所有新的开发使用此默认策略,因为递归引入了不必要的复杂性,并使代码更容易发生死锁。 若要简化从使用 Monitor 或 ReaderWriterLock的现有项目的迁移,可以使用 LockRecursionPolicy.SupportsRecursion 标志创建允许递归的 ReaderWriterLockSlim 实例。
线程可以在三种模式下进入锁定:读取模式、写入模式和可升级读取模式。 (在本主题的其余部分中,"可升级的读取模式" 称为 "可升级模式",将优先使用 "输入 x
模式" 短语来 "在 x
模式下进入锁定"。)
不管递归策略如何,在任何时候都只能有一个线程处于写入模式。 当线程处于写入模式时,任何其他线程都不能在任何模式下进入锁定状态。 在任何时候,只能有一个线程处于可升级模式。 任意数量的线程都可以处于读取模式,并且在其他线程处于读取模式时,可以有一个处于可升级模式的线程。
重要
此类型实现 IDisposable 接口。 在使用完类型后,您应直接或间接释放类型。 若要直接释放类型,请在 Disposetry
/ 块中调用其 catch
方法。 若要间接释放类型,请使用 using
(在 C# 中)或 Using
(在 Visual Basic 中)等语言构造。 有关详细信息,请参阅 IDisposable 接口主题中的“使用实现 IDisposable 的对象”一节。
ReaderWriterLockSlim 具有托管线程关联;也就是说,每个 Thread 对象都必须进行自己的方法调用来进入和退出锁模式。 任何线程都无法更改另一个线程的模式。
如果 ReaderWriterLockSlim 不允许使用递归,尝试进入锁定的线程可能会出于多种原因而阻塞:
-
如果有等待进入写入模式的线程或在写入模式下有单个线程,则尝试进入读取模式的线程会被阻止。
备注
在编写器排队时阻止新的读取器是优先写入器的锁公平策略。 当前的公平策略在最常见的情况下,将公平与读者和编写者进行平衡,以提高吞吐量。 未来版本的 .NET Framework 可能会引入新的公平策略。
-
如果已存在处于可升级模式的线程,则为尝试进入可升级模式的线程,如果存在等待进入写入模式的线程,则为; 如果在写入模式下有单个线程,则为。
-
如果在三种模式中的任何一种模式下有线程,则尝试进入写入模式块的线程。