自旋和线程所有权及递归构成混合锁
1 /// <summary> 2 /// 自旋、线程所有权、递归 3 /// 构成混合锁 4 /// </summary> 5 internal sealed class AnotherHybridLock : IDisposable 6 { 7 //基元用户模式构造(Interlocked的方法)使用 8 private int m_waiters = 0; 9 10 //基元内核模式构造 11 //AutoResetEvent通知正在等待的线程已发生事件 12 // true将初始状态设置为终止状态; false将初始状态设置为非终止 13 //true有信号(不会阻塞),false无信号 14 private AutoResetEvent m_waiterLock = new AutoResetEvent(false); 15 16 //控制自旋,希望能提升性能 17 //选择一个计数 18 private int m_spinCount = 4000; 19 20 //指出哪个线程拥有锁 21 private int m_owningThreadId = 0; 22 23 //指出拥有锁的线程拥有了多少次锁 24 private int m_recursion = 0; 25 26 public void Enter() 27 { 28 //获取当前托管线程的唯一标识符 29 int threadId = Thread.CurrentThread.ManagedThreadId; 30 31 //如果调用线程已经拥有锁 32 if (threadId == m_owningThreadId) 33 { 34 //递增递归计数并返回 35 m_recursion++; 36 return; 37 } 38 39 //调用线程未拥有锁,尝试获取它 40 //为基于自旋的等待提供支持 41 SpinWait spinWait = new SpinWait(); 42 for (int spinCount = 0; spinCount < m_spinCount; spinCount++) 43 { 44 //第一个线程时m_waiters始终返回0,直接获得锁 45 if (Interlocked.CompareExchange(ref m_waiters, 1, 0) == 0) 46 { 47 goto GotLock; 48 } 49 50 //给其它线程运行的机会,希望锁会被释放 51 spinWait.SpinOnce(); 52 } 53 54 //自旋结束,锁仍未获得,再试一次 55 //m_waiters若等于0,则1>1不成立,不必WaitOne,直接获得锁 56 //否则WaitOne 57 if (Interlocked.Increment(ref m_waiters) > 1) 58 { 59 //仍然是竞争条件,这个线程必须被阻塞 60 //等待锁,性能有较大损失 61 m_waiterLock.WaitOne(); 62 //等这个线程醒来时,它拥有锁,设置一些状态并返回 63 } 64 65 GotLock: 66 //线程获得锁时,记录线程Id 67 m_owningThreadId = threadId; 68 //指出该线程拥有锁1次 69 m_recursion = 1; 70 } 71 72 public void Leave() 73 { 74 //如果调用线程不拥有锁,则表明存在bug 75 int threadId = Thread.CurrentThread.ManagedThreadId; 76 if (threadId != m_owningThreadId) 77 { 78 //当某个方法要求调用方拥有给定 Monitor 上的锁并且该方法由不拥有该锁的调用方调用时引发的异常 79 throw new SynchronizationLockException("Lock not owned by calling thread"); 80 } 81 82 //递减递归计数 83 --m_recursion; 84 //如果该线程仍然拥有锁 85 if (m_recursion > 0) 86 { 87 //直接返回 88 return; 89 } 90 91 //执行到这里说明现在没有线程拥有锁 92 m_owningThreadId = 0; 93 94 //如果没有其它线程在等待则直接返回 95 if (Interlocked.Decrement(ref m_waiters) == 0) 96 { 97 return; 98 } 99 100 //否则,有其它线程正在等待,唤醒其中一个 101 //这里会有较大的性能损失 102 m_waiterLock.Set(); 103 } 104 105 public void Dispose() 106 { 107 m_waiterLock.Dispose(); 108 } 109 }