发布0101
大石头 编写于 2020-01-01 23:35:09
X
using System;
using System.Threading;
using NewLife.Threading;

namespace NewLife.Caching
{
    /// <summary>分布式锁</summary>
    public class CacheLock : DisposeBase
    {
        private ICache Client { get; set; }

        /// <summary>键</summary>
        public String Key { get; set; }

        /// <summary>实例化</summary>
        /// <param name="client"></param>
        /// <param name="key"></param>
        public CacheLock(ICache client, String key)
        {
            Client = client;
            Key = "lock:" + key;
        }

        /// <summary>申请锁</summary>
        /// <param name="msTimeout"></param>
        /// <returns></returns>
        public Boolean Acquire(Int32 msTimeout)
        {
            var ch = Client;
            var now = TimerX.Now;
            var sw = new SpinWait();

            // 循环等待
            var end = now.AddMilliseconds(msTimeout);
            while (true)
            {
                var expire = now.AddMilliseconds(msTimeout);

                // 申请加锁。没有冲突时可以直接返回
                var rs = ch.Add(Key, expire);
                if (rs) return true;

                now = DateTime.Now;
                if (now > end) break;

                // 死锁超期检测
                var dt = ch.Get<DateTime>(Key);
                if (dt <= now)
                {
                    // 开抢死锁。所有竞争者都会修改该锁的时间戳,但是只有一个能拿到旧的超时的值
                    expire = now.AddMilliseconds(msTimeout);
                    var old = ch.Replace(Key, expire);
                    // 如果拿到超时值,说明抢到了锁。其它线程会抢到一个为超时的值
                    if (old <= now) return true;
                }

                // 没抢到,继续
                //Thread.Sleep(20);
                sw.SpinOnce();
            }

            return false;
        }

        /// <summary>销毁</summary>
        /// <param name="disposing"></param>
        protected override void Dispose(Boolean disposing)
        {
            base.Dispose(disposing);

            Client.Remove(Key);
        }
    }
}