zoukankan      html  css  js  c++  java
  • Redis SETNX实现分布式锁

    1、某进程1执行 SETNX lock 以尝试获取锁

    2、由于某进程2已获得了锁,所以进程1执行 SETNX lock 返回0,即获取锁失败

    3、进程1执行 GET lock 来检测锁是否已超时,如果没超时,则线程等待一段时间,再次检测

    4、如果进程1检测到锁已超时,即当前的时间大于键 lock 的值,进程1会执行以下操作

    GETSET lock <current Unix timestamp + lock timeout + 1>

    5、由于 GETSET 操作在设置键的值的同时,还会返回键的旧值,通过比较键 lock 的旧值是否小于当前时间,可以判断进程是否已获得锁

    6、假如另一个进程3也检测到锁已超时,并在进程1之前执行了 GETSET 操作,那么进程1的 GETSET 操作返回的是一个大于当前时间的时间戳,这样进程1就不会获得锁而继续等待。注意到,即使进程1接下来将键 lock 的值设置了比进程3设置的更大的值也没影响。

    另外,值得注意的是,在进程释放锁,即执行 DEL lock 操作前,需要先判断锁是否已超时。如果锁已超时,那么锁可能已由其他进程获得,这时直接执行 DEL lock 操作会导致把其他进程已获得的锁释放掉。

    C# Code

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using CSRedis;
    
    namespace RedisLockDemo
    {
        public class CsRedisLock
        {
            private static readonly int _lock_timeout = 40;
            private static readonly string _lock_key = "lock";
            public static void Test()
            {
                var rds = new CSRedisClient("127.0.0.1:6379,password=123456,defaultDatabase=13,poolsize=50,ssl=false");
                RedisHelper.Initialization(rds);
    
                Parallel.For(0, 13, x =>
                {
                    if (GetLock(_lock_key))
                    {
                        Console.WriteLine($"person:{x},线程ID:{Thread.CurrentThread.ManagedThreadId},获得锁 woking");
    
                        if (DateTimeOffset.Now.ToUnixTimeMilliseconds() < RedisHelper.Get<long>(_lock_key))
                        {
                            //释放锁
                            RedisHelper.Del(_lock_key);
                        }
                    }
                    else
                    {
                        Console.WriteLine($"person:{x},线程ID:{Thread.CurrentThread.ManagedThreadId},获取锁异常");
                    }
                });
                Console.WriteLine();
            }
    
            private static bool GetLock(string key)
            {
                bool getLocked = false;
                try
                {
                    while (!getLocked)
                    {
                        var now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                        var lock_time = now + _lock_timeout + 1;
                        getLocked = RedisHelper.SetNx(key, lock_time);
                        //判断是否获取锁,
                        if (getLocked || now > RedisHelper.Get<long>(key) && now > RedisHelper.GetSet<long>(key, lock_time))
                        {
                            getLocked = true;
                        }
                        else
                        {
                            Thread.Sleep(30);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                return getLocked;
            }
        }
    }
    

    相关文档:https://redis.io/commands/setnx

  • 相关阅读:
    web----WSGI
    ovs 实现vlan隔离(一)
    ovs流表机制(四)用vxlan实现不同网段通信
    ovs流表机制(四)用vxlan实现同网段通信
    ovs 流表机制(三)--group表
    ovs 流表机制(二)-OVS流表table之间的跳转
    ovs 流表机制(一)
    euler ironic镜像驱动问题(一)镜像启动失败报dracut initqueue timeout
    ovs流表
    arm64 uefi启动
  • 原文地址:https://www.cnblogs.com/rohmeng/p/11397476.html
Copyright © 2011-2022 走看看