在 ExitWriterLock 之后释放 ReaderWriterLockSlim

Dispose ReaderWriterLockSlim after ExitWriterLock

提问人:leuction 提问时间:5/13/2022 最后编辑:leuction 更新时间:5/14/2022 访问量:238

问:

我有一个清理一些对象以及 ReaderWriterLockSlim 的功能。但是我需要 ReaderWriterLockSlim 锁定为写入器锁,以防止另一个线程在我进行清理时读取数据。

ConcurrentDictionary<string, ReaderWriterLockSlim> RwLocks = new ConcurrentDictionary<string, ReaderWriterLockSlim>();

private ReaderWriterLockSlim GetRwLock(string key)
{
    return RwLocks.GetOrAdd(key, _ => new ReaderWriterLockSlim());
}

public void CleanUp(string key)
{
    ReaderWriterLockSlim rwLock = this.GetRwLock(key);

    try 
    {
        rwLock.EnterWriterLock();
        // do some other clean up
        this.RwLocks.TryRemove(key, out _);
    }
    finally
    {
        rwLock.ExitWriterLock();
        // It is safe to do dispose here?
        // could other thread enter the read lock or writer lock here?
        // and the dispose will throw exceptions?
        // What is the best practice to do the dispose?
        rwLock.Dispose();
    }
}

我有一个想法来包装.你认为它可以解决问题还是有任何潜在风险ReaderWriterLockSlim

public class ReaderWriterLockSlimWrapper
{
    private ReaderWriterLockSlim rwLock;

    private volatile bool disposed;

    public ReaderWriterLockSlimWrapper()
    {
        rwLock = new ReaderWriterLockSlim();
        disposed = false;
    }

    private void DisposeInternal()
    {
        if (!rwLock.IsReadLockHeld && !rwLock.IsUpgradeableReadLockHeld && !rwLock.IsWriteLockHeld)
        {
            rwLock.Dispose();
        }
    }

    public void Dispose()
    {
        disposed = true;

        DisposeInternal();
    }

    public void EnterReadLock()
    {
        rwLock.EnterReadLock();
    }

    public void ExitReadLock()
    {
        rwLock.ExitReadLock();

        if (disposed)
        {
            DisposeInternal();
        }
    }

    public void EnterWriteLock()
    {
        rwLock.EnterWriteLock();
    }

    public void ExitWriteLock()
    {
        rwLock.ExitWriteLock();

        if (disposed)
        {
            DisposeInternal();
        }
    }
}
C# 多线程 ConcurrentDictionary ReadWriteLock ReaderWriterLockSlim

评论

0赞 Peter Csala 5/13/2022
由于您已从集合中删除了 RWL,并且 GetRwLock 会根据需要创建一个新 RWL,因此在此处调用 Dispose 似乎没问题。
0赞 Theodor Zoulias 5/13/2022
相关:基于键/ConcurrentDictionary 的异步锁定每个键有多个值,删除空条目。不管怎样,这是一项棘手的业务。在程序完成之前让 s 闲逛要简单得多。ReaderWriterLockSlim
0赞 leuction 5/13/2022
@PeterCsala 但是当我们退出写入器锁时,其他线程可能会被 EnterReadLock() 阻止。其他线程会进入读卡器锁还是尝试再次获取读卡器锁?
1赞 Damien_The_Unbeliever 5/13/2022
我会从这里退后一步 - 为什么,一方面你有一些代码知道现在是调用的“正确时间”,另一方面,你不知道其他线程是否会尝试进入锁?这不应该是你一开始就陷入的情况。CleanUp
1赞 Damien_The_Unbeliever 5/14/2022
好吧,在这一点上,您似乎在描述某种缓存。目前尚不清楚为什么您要尝试自己构建它,而不是使用已经构建的缓存系统,因此,再次退后一步。

答:

0赞 Theodor Zoulias 5/14/2022 #1

您尚未描述打算使用两种机制的具体方案,无论是第一种机制还是 / 方法,第二种机制都是类。所以我想问题是:CleanUpGetRwLockReaderWriterLockSlimWrapper

我的两种机制是否可以在所有可能的多线程方案中安全使用,其中线程安全和操作原子性是强制性的?

答案是否定的,你们的两种机制都充斥着竞争条件,并且不能保证原子性。在多线程方案中使用它们会导致未定义的行为,包括但不限于:

  1. 意外的异常。
  2. 违反正确使用的 ReaderWriterLockSlim 通常应强制执行的策略。换句话说,两个线程可能会同时获取同一密钥的写入器锁,或者与已获取同一密钥的读取器锁的其他线程同时获取,或两者兼而有之。

解释为什么你的机制是有缺陷的,这是相当复杂的。一般的解释是,每当您在多线程环境中使用该模式时,尽管 和 可能是单独的线程安全的,但您允许第二个线程在两个调用之间抢占当前线程,并使第一个检查的结果无效。if (x.BooleanProperty) x.Method();BooleanPropertyMethod

顺便说一句,请注意 不是跨进程同步原语。因此,即使您修复了机制,然后尝试在 Web 应用程序中使用它们,这些策略仍可能被违反,因为 Web 服务器可能会在随机时刻决定回收当前进程并启动新进程。在这种情况下,Web 应用程序可能会在两个进程上同时运行,运行时间跨度为几秒钟甚至几分钟。ReaderWriterLockSlim