C# Redis分布式锁 - 单节点( 二 )


//set : key存在则失败,不存在才会成功,并且过期时间5秒var success = redisClient.Set(lockKey, 1, expireSeconds: 5, exists: RedisExistence.Nx);这个问题虽然解决了,但随之产生了一个新的问题:
【C# Redis分布式锁 - 单节点】假设有3个线程A,B,C
当线程A拿到锁后执行业务的时候超时了,超过了锁的过期时间还没执行完,这时候锁被Redis释放了,
于是线程B拿到了锁并开始执行业务逻辑.
当线程B的业务逻辑还没执行完的时候,线程A的业务逻辑执行完了,于是乎就跑去释放掉了锁.
这时候线程C就可以拿到锁开始执行它的业务逻辑.
这不就乱套了么...
因此,线程在释放锁的时候应该判断这个锁还属不属于自己.
所以,在设置锁的时候,redis的value值不能像上面代码那样,随便给个1,而应该给一个随机值,代表当前线程.
C# Redis分布式锁 - 单节点文章插图
var id = Guid.NewGuid().ToString("N");//获取锁do{//set : key存在则失败,不存在才会成功,并且过期时间5秒var success = redisClient.Set(lockKey, id, expireSeconds: 5, exists: RedisExistence.Nx);if (success == true){break;}Thread.Sleep(TimeSpan.FromSeconds(1));//休息1秒再尝试获取锁} while (true);Console.WriteLine($"线程:{Task.CurrentId} 拿到了锁,开始消费");.........//业务处理完后,释放锁.var value = http://kandian.youth.cn/index/redisClient.Get(lockKey);if (value == id){redisClient.Del(lockKey);}
C# Redis分布式锁 - 单节点文章插图
完美了吗?
不完美.还是老生常谈的问题,取value和删除key 分了两步走,不是原子操作.
并且,这里还不能用pipe,因为需要根据取到的value来决定下一个操作.上面设置过期时间倒是可以用pipe.
所以,这里只能用lua.
完整的代码如下:
C# Redis分布式锁 - 单节点文章插图
CSRedisClient redisClient = new CSRedis.CSRedisClient("127.0.0.1:6379,defaultDatabase=0");var lockKey = "lockKey";var stock = 5;//商品库存var taskCount = 10;//线程数量var script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";//释放锁的redis脚本redisClient.Del(lockKey);//测试前,先把锁删了.for (int i = 0; i < taskCount; i++){Task.Run(() =>{var id = Guid.NewGuid().ToString("N");//获取锁do{//set : key存在则失败,不存在才会成功,并且过期时间5秒var success = redisClient.Set(lockKey, id, expireSeconds: 5, exists: RedisExistence.Nx);if (success == true){break;}Thread.Sleep(TimeSpan.FromSeconds(1));//休息1秒再尝试获取锁} while (true);Console.WriteLine($"线程:{Task.CurrentId} 拿到了锁,开始消费");if (stock <= 0){Console.WriteLine($"库存不足,线程:{Task.CurrentId} 抢购失败!");redisClient.Eval(script,lockKey,id);return;}stock--;//模拟处理业务,这里不考虑失败的情况Thread.Sleep(TimeSpan.FromSeconds(new Random().Next(1, 3)));Console.WriteLine($"线程:{Task.CurrentId} 消费完毕!剩余 {stock} 个");//业务处理完后,释放锁.redisClient.Eval(script, lockKey, id);});}
C# Redis分布式锁 - 单节点文章插图
这篇文章只介绍了单节点Redis的分布式锁,因为单节点,所以不是高可用.