1、简述出现背景
Python 基于Redis实现一个简单的分布式锁
前面实现了一个简单的分布式锁,它在某些场景下可以有效的处理并发写数据问题。但是如果我们在某个线程中需要二次或多次获得同一个锁,这个分布式锁就会出现死锁,一直等待第一个加锁逻辑结束后才能继续进行,并且会直接导致当前的业务逻辑的异常和超时。因此还需要实现一种分布式可重入锁,来应对这种场景。
2、简述原理
简述一下可重入锁实现原理:首先在加锁时,进行判断是否能获得锁,如果获得锁,那么此时维护一个计数器,并在获得锁时设置计数器值为0。然后如果是同一个线程再次请求获得锁,那么就将计数器加一。在线程释放锁时有两种情况,第一种是重入时的释放锁,此时将维护的计数器减一即可。第二种是当计数器为0,且确实时目标线程释放锁时,此时真正的释放掉锁。流程结束。
3、代码参考
# redis_return_lock.py
import redis
import time
import threading
# 连接池方式
# pool = redis.ConnectionPool(host='127.0.0.1',port=6379)
pool = redis.ConnectionPool().from_url('redis://127.0.0.1:6379/2')
con = redis.Redis(connection_pool=pool)
class RedisLock(object):
def __init__(self):
self.redis_con = con
def get_lock(self, val):
while True:
res = self.redis_con.set('lock', val, nx=True, ex=60)
if res:
self.redis_con.set(val, '0', nx=True, ex=60)
break
else:
old_val = self.redis_con.get('lock')
if old_val.decode() == val:
self.redis_con.incr(val)
break
# print('继续等待锁...')
time.sleep(0.1)
def del_lock(self, val):
old_val = self.redis_con.get('lock')
num = self.redis_con.get(val)
if old_val == val.encode() and num.decode() == '0':
self.redis_con.delete('lock')
print('锁释放成功')
elif old_val == val.encode() and num.decode() != '0':
print('重入锁内释放锁,计数器减一...')
self.redis_con.decr(val)
SUMS = 0
def test_lock(name, num, val):
try:
lock = RedisLock()
print('%s 开始工作' % name)
print('%s 准备获取锁并加锁' % name)
lock.get_lock(val)
def inner_test(lock):
lock.get_lock(val)
print('测试重入锁...')
lock.del_lock(val)
inner_test(lock)
print('%s 得到锁,继续工作' % name)
global SUMS
SUMS += 15
time.sleep(num)
print(SUMS)
except Exception as e:
print('发生异常:%s' % str(e))
finally:
print('%s 操作完成,准备释放锁'%name)
lock.del_lock(val)
if __name__ == '__main__':
start_time = time.time()
tasks = []
for num in range(1,4):
t = threading.Thread(target=test_lock, args=('任务%d'%num,num,'lock%d'%num,))
tasks.append(t)
t.start()
[item.join() for item in tasks]
print('总耗时:', time.time() - start_time)
4、运行测试
python redis_return_lock.py
结果:
任务1 开始工作
任务1 准备获取锁并加锁
任务2 开始工作
任务2 准备获取锁并加锁
任务3 开始工作
任务3 准备获取锁并加锁
测试重入锁...
重入锁内释放锁,计数器减一...
任务1 得到锁,继续工作
15
任务1 操作完成,准备释放锁
锁释放成功
测试重入锁...
重入锁内释放锁,计数器减一...
任务3 得到锁,继续工作
30
任务3 操作完成,准备释放锁
锁释放成功
测试重入锁...
重入锁内释放锁,计数器减一...
任务2 得到锁,继续工作
45
任务2 操作完成,准备释放锁
锁释放成功
总耗时: 6.0716187953948975