NOTE: The examples provided are susceptible to Deadlocks. Example:
QueuedLock queuedLock = new QueuedLock(); void func1() { try { queuedLock.Enter(); fubc2() } finally { queuedLock.Exit(); } } void func2() { try { queuedLock.Enter(); //<<<< DEADLOCK } finally { queuedLock.Exit(); } }
Re. optional solution (inc. an optional IDisposable usage):
public sealed class QueuedLock { private class SyncObject : IDisposable { private Action m_action = null; public SyncObject(Action action) { m_action = action; } public void Dispose() { lock (this) { var action = m_action; m_action = null; action?.Invoke(); } } } private readonly object m_innerLock = new Object(); private volatile uint m_ticketsCount = 0; private volatile uint m_ticketToRide = 1; public bool Enter() { if (Monitor.IsEntered(m_innerLock)) return false; uint myTicket = Interlocked.Increment(ref m_ticketsCount); Monitor.Enter(m_innerLock); while (true) { if (myTicket == m_ticketToRide) return true; Monitor.Wait(m_innerLock); } } public void Exit() { Interlocked.Increment(ref m_ticketToRide); Monitor.PulseAll(m_innerLock); Monitor.Exit(m_innerLock); } public IDisposable GetLock() { if (Enter()) return new SyncObject(Exit); return new SyncObject(null); } }
Usage:
QueuedLock queuedLock = new QueuedLock(); void func1() { bool isLockAquire = false; try { isLockAquire = queuedLock.Enter(); // here code which needs to be synchronized in correct order } finally { if (isLockAquire) queuedLock.Exit(); } }
or:
QueuedLock queuedLock = new QueuedLock(); void func1() { using (queuedLock.GetLock()) { // here code which needs to be synchronized in correct order } }