使用ReaderWriterLockSlim

时间:2019-04-02 05:11:08

标签: c# .net readerwriterlockslim

我在ReaderWriterLockSlim周围写了一个相当琐碎的包装:

class SimpleReaderWriterLock
{
    private class Guard : IDisposable
    {
        public Guard(Action action)
        {
            _Action = action;
        }

        public void Dispose()
        {
            _Action?.Invoke();
            _Action = null;
        }

        private Action _Action;
    }

    private readonly ReaderWriterLockSlim _Lock
        = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

    public IDisposable ReadLocked()
    {
        _Lock.EnterReadLock();
        return new Guard(_Lock.ExitReadLock);
    }

    public IDisposable WriteLocked()
    {
        _Lock.EnterWriteLock();
        return new Guard(_Lock.ExitWriteLock);
    }

    public IDisposable UpgradableReadLocked()
    {
        _Lock.EnterUpgradeableReadLock();
        return new Guard(_Lock.ExitUpgradeableReadLock);
    }
}

(这可能不是世界上最有效的方法,因此我也对改进此类的建议感兴趣。)

它的用法如下:

using (_Lock.ReadLocked())
{
    // protected code
}

(有很多读操作非常频繁,几乎从来没有写。)

在发布模式和生产环境中,这似乎总是可以正常工作。但是,在调试模式和调试器中,进程有时会以特殊状态死锁-它被称为EnterReadLock,该锁本身不被任何东西持有(所有者为0,报告其是否具有锁的属性任何读者/作家/服务员都不会这样,等等)但是里面的自旋锁已被锁定,并且在那里不断旋转。

我不知道是什么触发了这一点,除了如果我在断点处停止并单步执行(在完全不相关的代码中),它似乎更经常发生。

如果我将自旋锁_isLocked字段手动切换回0,则该过程将恢复,并且随后一切正常。

代码或锁本身有问题吗?调试器是否正在执行某些操作以意外引发自旋锁死锁? (我使用的是.NET 4.6.2。)

我已经读过an article,这表明ThreadAbortException可能是这些锁的问题-我的代码确实在某些地方调用了Abort(),但是我没有t think 涉及那些调用此锁定代码的代码(尽管我可能会误解),如果问题是该锁已被获取且从未释放过,那么它的外观应与我所看到的有所不同。 (顺便说一句,框架文档特别禁止在受限区域中获取锁,如该文章所鼓励。)

可以更改代码以避免间接锁,但是using总体上不是在保护推荐的做法吗?

1 个答案:

答案 0 :(得分:0)

由于using语句is not abort-safe,您可以尝试用linked article中建议的中止安全解决方法来代替它。像这样:

public void WithReadLock(Action action)
{
    var lockAcquired = false;
    try
    {
        try { }
        finally
        {
            _Lock.EnterReadLock();
            lockAcquired = true;
        }
        action();
    }
    finally
    {
        if (lockAcquired) _Lock.ExitReadLock();
    }
}

用法:

var locker = new SimpleReaderWriterLock();
locker.WithReadLock(() =>
{
    // protected code
});