混合锁的特征是在获取锁失败后像自旋锁一样重试一定的次数,超过一定次数之后(.NET Core 2.1 是30次)再安排当前进程进入等待状态
混合锁的好处是,如果第一次获取锁失败,但其他线程马上释放了锁,当前线程在下一轮重试可以获取成功,不需要执行毫秒级的线程调度处理;而如果其他线程在短时间内没有释放锁,线程会在超过重试次数之后进入等待状态,以避免消耗 CPU 资源,因此混合锁适用于大部分场景
internal sealed class SimpleHybridLock : IDisposable { //基元用户模式构造使用 private int m_waiters = 0; //基元内核模式构造 private AutoResetEvent m_waiterLock = new AutoResetEvent(false); public void Enter() { //指出该线程想要获得锁 if (Equals(Interlocked.Increment(ref m_waiters), 1)) { //无竞争,直接返回 return; } //另一个线程拥有锁(发生竞争),使这个线程等待 //线程会阻塞,但不会在CPU上“自旋”,从而节省CPU //这里产生较大的性能影响(用户模式与内核模式之间转换) //待WaitOne返回后,这个线程拿到锁 m_waiterLock.WaitOne(); } public void Leave() { //该线程准备释放锁 if (Equals(Interlocked.Decrement(ref m_waiters), 0)) { //无线程等待,直接返回 return; } //有线程等待则唤醒其中一个 //这里产生较大的性能影响(用户模式与内核模式之间转换) m_waiterLock.Set(); } public void Dispose() { m_waiterLock.Dispose(); } } public static class MonitorSample { private static readonly object Lock = new object(); private static int _counterA = 0; private static int _counterB = 0; public static void IncrementCounterA() { var lockObj = Lock; var lockTaken = false; try { // 获取锁 Monitor.Enter(lockObj, ref lockTaken); // 锁保护 { _counterA++; _counterB++; } } finally { // 释放锁 if(lockTaken) Monitor.Exit(lockObj); } } public static void IncrementCounterB() { lock (Lock) { _counterA++; _counterB++; } } }图示: