30.2 自旋、线程所有权和递归

    /// <summary>
    /// 其他混合锁
    /// </summary>
    internal sealed class AnotherHybridLock : IDisposable
    {
        //由基元用户模式构造(Interlocked的方法)使用
        private int _waiters = 0;

        //AutoResetEvent 是基元内核模式构造
        private AutoResetEvent _waiterLock = new AutoResetEvent(false);

        //这个字段控制自旋,希望提升性能
        private int _spinCount = 4000;      //随便选择的一个计数

        //这些字段指出哪个线程拥有锁,以及拥有了它多少次
        private int _owingThreadId = 0, _recursion = 0;

        public void Enter()
        {
            //如果调用线程已经拥有锁,递增递归计数并返回
            int threadId = Thread.CurrentThread.ManagedThreadId;
            if (threadId == _owingThreadId)
            {
                _recursion++;
                return;
            }

            //调用的线程不再拥有锁,尝试获取它
            SpinWait spinwait = new SpinWait();
            for (int i = 0; i < _spinCount; i++)
            {
                //如果锁可以自由使用了,这个线程就获得它:设置一些状态并返回
                if (Interlocked.CompareExchange(ref _waiters, 1, 0) == 0)
                    goto GotLock;

                //黑科技:给其他线程运行的机会,希望锁会被释放
                spinwait.SpinOnce();
            }

            //自旋结束,锁仍未获得,再试一次
            if (Interlocked.Increment(ref _waiters) > 1)
            {
                //仍然是竞态条件,这个线程必须阻塞
                _waiterLock.WaitOne();  //等待锁:性能有损失
                //等待这个线程醒来时,它拥有锁:设置一些状态并返回
            }

            GotLock:
            //一个线程获得锁时,我们记录它的ID,并指出线程拥有锁一次
            _owingThreadId = threadId;
            _recursion = 1;
        }

        public void Leave()
        {
            //如果调用线程不再拥有锁,表明存在bug
            int threadId = Thread.CurrentThread.ManagedThreadId;
            if (threadId != _owingThreadId)
                throw new SynchronizationLockException("Lock not owned by calling thread");

            //递减递归计数。如果这个线程仍然拥有锁,那么直接返回
            if (--_recursion > 0)
                return;

            _owingThreadId = 0; //现在没有线程拥有锁

            //如果没有其他线程在等待,直接返回
            if (Interlocked.Decrement(ref _waiters) == 0)
                return;

            //有其他线程正在阻塞,唤醒其中一个
            _waiterLock.Set();      //这里产生较大的性能影响
        }

        public void Dispose()
        {
            _waiterLock.Dispose();
        }
    }

猜你喜欢

转载自www.cnblogs.com/kikyoqiang/p/10227341.html