我正在尝试实现一种机制,其中自动检查锁定顺序,并且在运行时无序地获取锁以避免死锁时抛出异常。参考实现如下。如果您发现此实施有任何问题,请通知我。非常感谢。
public class someResource
{
private OrderedLock lock1 = new OrderedLock(1);
private OrderedLock lock2 = new OrderedLock(2);
public void lockInOrder()
{
lock1.AcquireWriteLock();
lock2.AcquireWriteLock();
// do something
lock1.ReleaseWriteLock();
lock2.ReleaseWriteLock();
}
public void lockOutOfOrder()
{
lock2.AcquireReadLock();
lock1.AcquireReadLock(); // throws exception
// read something
lock2.ReleaseReadLock();
lock1.ReleaseReadLock();
}
}
public class OrderedLock : IDisposable
{
private static readonly ConcurrentDictionary<int, object> createdLocks = new ConcurrentDictionary<int, object>();
[ThreadStatic]
private static ISet<int> acquiredLocks;
private readonly ThreadLocal<int> refCount = new ThreadLocal<int>(false);
private readonly ReaderWriterLockSlim locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
private readonly int id;
/// <exception cref="InvalidOperationException">Duplicate identifier detected</exception>
public OrderedLock(int id)
{
if (!createdLocks.TryAdd(id, null))
{
throw new InvalidOperationException("Duplicate identifier detected");
}
this.id = id;
this.refCount.Value = 0;
}
public void AcquireReadLock()
{
this.CheckLockOrder();
this.locker.EnterReadLock();
}
public void AcquireWriteLock()
{
this.CheckLockOrder();
this.locker.EnterWriteLock();
}
public void ReleaseReadLock()
{
this.refCount.Value--;
this.locker.ExitReadLock();
if (this.refCount.Value == 0)
{
acquiredLocks.Remove(this.id);
}
}
public void ReleaseWriteLock()
{
this.refCount.Value--;
this.locker.ExitWriteLock();
if (this.refCount.Value == 0)
{
acquiredLocks.Remove(this.id);
}
}
public void Dispose()
{
while (this.locker.IsWriteLockHeld)
{
this.ReleaseWriteLock();
}
while (this.locker.IsReadLockHeld)
{
ReleaseReadLock();
}
this.locker.Dispose();
this.refCount.Dispose();
GC.SuppressFinalize(this);
}
/// <exception cref="InvalidOperationException">Invalid order of locking detected</exception>
private void CheckLockOrder()
{
if (acquiredLocks == null)
{
acquiredLocks = new HashSet<int>();
}
if (!acquiredLocks.Contains(this.id))
{
if (acquiredLocks.Any() && acquiredLocks.Max() > this.id)
{
throw new InvalidOperationException("Invalid order of locking detected");
}
acquiredLocks.Add(this.id);
}
this.refCount.Value++;
}
}