地址: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 可能会引入新的公平策略。

  • 如果已存在处于可升级模式的线程,则为尝试进入可升级模式的线程,如果存在等待进入写入模式的线程,则为; 如果在写入模式下有单个线程,则为。

  • 如果在三种模式中的任何一种模式下有线程,则尝试进入写入模式块的线程。