基于Thread.SpinWait实现自旋锁
实现原理:基于Test--And--Set原子操作实现
使用一个数据表示当前锁是否已经被获取 0表示未被索取,1表示已经获取 获取锁时会将_lock的值设置为1 然后检查修改前的值是否等于0,
优点:
缺点:
当前实现没有考虑到公平性,如果多个线程同时获取锁失败,按时间顺序第一个获取锁的线程不一定会在释放锁后第一个获取成功,
代码实现:
public static class ThreadSpinWaitDemo { private static int _lock = 0; private static int _counterA = 0; private static int _counterB = 0; public static void IncrementCounters() { while (Interlocked.Exchange(ref _lock, 1) != 0) { Thread.SpinWait(1); } ++_counterA; ++_counterB; Interlocked.Exchange(ref _lock, 0); } public static void GetCounters(out int counterA, out int counterB) { while (Interlocked.Exchange(ref _lock, 1) != 0) { Thread.SpinWait(1); } counterA = _counterA; counterB = _counterB; Interlocked.Exchange(ref _lock, 0); } }
特性是SpinOnce方法的次数,如果在一定次数以内并且当前逻辑核心所大于1,则调用Thread.SpinWait函数;如果超过一定次数或者当前环境逻辑核心数等于1,则交替使用
Thread.Sleep(0)和Thread.Yield函数,表示切换到其他线程,如果再超过一定次数,则让当前线程休眠
SpinWaite解决Thread.SpinWait中的两个问题
public static class ThreadSpinOnceDemo { private static int _lock = 0; private static int _counterA = 0; private static int _counterB = 0; public static void IncrementCounters() { var spinWait = new SpinWait(); while (Interlocked.Exchange(ref _lock, 1) != 0) { spinWait.SpinOnce(); } ++_counterA; ++_counterB; Interlocked.Exchange(ref _lock, 0); } public static void GetCounters(out int counterA, out int counterB) { var spinWait = new SpinWait(); while (Interlocked.Exchange(ref _lock, 1) != 0) { spinWait.SpinOnce(); } counterA = _counterA; counterB = _counterB; Interlocked.Exchange(ref _lock, 0); } }
封装了SpinWaite的逻辑
SpinLock代码实现
public class ThreadSpinLockDemo { private static SpinLock _spinLock = new SpinLock(); private static int _counterA = 0; private static int _counterB = 0; public static void IncrementCounters() { bool lockTaken = false; try { _spinLock.Enter(ref lockTaken); ++_counterA; ++_counterB; } finally { if (lockTaken) { _spinLock.Exit(); } } } public static void GetCounters(out int counterA, out int counterB) { bool lockTaken = false; try { _spinLock.Enter(ref lockTaken); counterA = _counterA; counterB = _counterB; } finally { if (lockTaken) { _spinLock.Exit(); } } } }