【问题标题】:Mysterious deadlock corruption with ReaderWriterLockSlimReaderWriterLockSlim 的神秘死锁损坏
【发布时间】:2019-04-02 05:11:08
【问题描述】:

我在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,报告它是否有任何读者的属性/writers/waiters 说不,等等)但是里面的自旋锁被锁住了,它在那里无休止地旋转。

我不知道是什么触发了这种情况,但如果我在断点处停止并单步执行(在完全不相关的代码中),它似乎会更频繁地发生。

如果我手动将自旋锁 _isLocked 字段切换回 0,则该过程将恢复,之后一切似乎都按预期工作。

密码或锁本身有问题吗?调试器是否正在做一些事情来意外引发自旋锁死锁? (我使用的是 .NET 4.6.2。)

我读过an article,这表明ThreadAbortException 可能是这些锁的问题——我的代码在某些地方确实调用了Abort()——但我不认为 那些涉及调用此锁定代码的代码(尽管我可能弄错了),如果问题是锁已被获取但从未释放,那么它应该与我所看到的不同。 (顺便说一句,框架文档明确禁止在受限区域中获取锁定,正如那篇文章所鼓励的那样。)

我可以更改代码以避免锁定间接,但using 不是一般推荐的做法吗?

【问题讨论】:

  • 如果我打电话给WriteLocked然后忘记打电话给Dispose会发生什么?
  • @mjwills 你所期望的:它会在下一次读/写时死锁。 但是这不是这里发生的事情,因为这样外部锁就会显示为已锁定,而事实并非如此。

标签: c# .net readerwriterlockslim


【解决方案1】:

由于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
});

【讨论】:

    猜你喜欢
    • 2013-11-24
    • 2010-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-30
    • 2020-12-03
    相关资源
    最近更新 更多