import time import uuid import redis from threading import Thread redis_client = redis.Redis(host='localhost', port=6379) # 获取一个锁 def acquicre_lock(lock_name, acquice_time=10, time_out=10): """ 获取一个分布式锁 :param lock_name: 锁名称 :param acquice_time: 客户端等待获取锁的时间 :param time_out: 锁的超时时间 :return: """ # 生成唯一id identifier = str(uuid.uuid4()) # 客户端获取锁的结束时间 end = time.time() + acquice_time # key lock = 'string:lock:' + lock_name while time.time() < end: # setnx(key, value) 只有key不存在情况下,将key值设置为value,若key存在则不做任何动作,返回True和False if redis_client.setnx(lock, identifier): # 给锁设置超时时间,防止进程崩溃导致其他进程无法获取锁 redis_client.expire(lock, time_out) return identifier # 当锁未被设置过期时间时,重新设置其过期时间 elif not redis_client.ttl(lock): redis_client.expire(lock, time_out) time.sleep(0.001) return False # 释放一个锁 def release_lock(lock_name, identifier): """ 通用的锁释放函数 """ lock = 'string:lock:' + lock_name pip = redis_client.pipeline(True) while True: try: # 通过watch命令监视某个键,当该键未被其他客户端修改值时,事务成功执行,当事务运行过程中,发现该值被其他客户端更新了值,任务失败 pip.watch(lock) lock_value = redis_client.get(lock) if not lock_value: return True if lock_value.decode() == identifier: # multi命令用于开启一个事务,它总是返回ok # multi执行之后, 客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中, 当 EXEC 命令被调用时, 所有队列中的命令才会被执行 pip.multi() # 删除键,释放锁 pip.delete(lock) # execute命令负责触发并执行事务中的所有命令 pip.execute() return True pip.unwatch() break except redis.exceptions.WatchError: # 释放锁期间,有其他客户端改变了键值对,锁释放失败,进行循环 pass return False count = 10 def seckill(i): identifier = acquicre_lock('resource') print('线程:{}--获得了锁'.format(i)) time.sleep(1) global count if count < 1: print('线程:{}--没抢到,票抢完了'.format(i)) return count -= 1 print('线程:{}--抢到一张票,还剩下{}张票'.format(i, count)) release_lock('resource', identifier) for i in range(50): t = Thread(target=seckill, args=(i,)) t.start()