Redis SETNX实现分布式锁
程序员文章站
2022-05-18 21:53:29
1、某进程1执行 SETNX lock 以尝试获取锁 2、由于某进程2已获得了锁,所以进程1执行 SETNX lock 返回0,即获取锁失败 3、进程1执行 GET lock 来检测锁是否已超时,如果没超时,则线程等待一段时间,再次检测 4、如果进程1检测到锁已超时,即当前的时间大于键 lock 的 ......
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; } } }
相关文档: