1 基本介绍
RLock递归锁是同步锁的一个升级版本,在同步锁的基础上可以做到连续重复多次acquire()后再多次realease()的操作。但是一定要注意加锁次数和解锁次数必须一致,否则也将引发死锁现象。 递归锁的内部维护着一个计数器,当计数器不为0的时候不能被I/O操作和时间轮询机制切换
2 常用方法
递归锁相关的方法如下:
| 方法 | 描述 |
|---|---|
| threading.RLock() | 返回一个递归锁对象 |
| threading.RLock().acquire(blocking=True, timeout=1) | 上锁,当一个线程在执行被上锁代码块时,将不允许切换到其他线程,默认失效时间为1秒 |
| threading.RLock().release() | 解锁,当一个线程在执行未被上锁代码块时,将允许系统根据策略自行切换到其他线程中运行 |
| threading.RLock().locked() | 判断该锁对象是否处于上锁状态,返回一个布尔值 |
3 使用示例
以下是递归锁使用的一个案例。这里是将前面同步锁中造成死锁的代码拿来使用递归锁进行运行。
import threading
import time
import random
class TicketDB:
def __init__(self):
"""
车票数量
"""
self.ticket_count = 5
def get_ticket_count(self):
"""
获取当前车票的数量
:return: 当前车票的数量
"""
return self.ticket_count
def sell_ticket(self):
# TODD:用户购票付款流程
sleep_time = random.randrange(1, 8)
time.sleep(sleep_time)
t = threading.current_thread()
print(f'{t.name}网点,已经售出第{self.ticket_count}张票')
self.ticket_count -= 1
db = TicketDB()
lock = threading.RLock()
def thread_body():
global db, lock
while True:
lock.acquire()
lock.acquire()
lock.acquire()
curr_ticket_count = db.get_ticket_count()
if curr_ticket_count > 0:
db.sell_ticket()
else:
lock.release()
lock.release()
lock.release()
break
lock.release()
lock.release()
lock.release()
time.sleep(1)
def main():
t1 = threading.Thread(target=thread_body)
t1.start()
t2 = threading.Thread(target=thread_body)
t2.start()
if __name__ == '__main__':
main()
result:
Thread-1网点,已经售出第5张票
Thread-2网点,已经售出第4张票
Thread-1网点,已经售出第3张票
Thread-2网点,已经售出第2张票
Thread-1网点,已经售出第1张票
可以看到。同步锁中造成死锁的代码在递归锁下可以正常执行。
4 with语句
与同步锁一样,threading.RLock()对象中也实现了enter() 与exit()方法,因此我们也可以像同步锁一样,使用with语句进行上下文管理形式的递归锁的加锁解锁操作。
示例如下:
import threading
import time
import random
class TicketDB:
def __init__(self):
"""
车票数量
"""
self.ticket_count = 5
def get_ticket_count(self):
"""
获取当前车票的数量
:return: 当前车票的数量
"""
return self.ticket_count
def sell_ticket(self):
# TODD:用户购票付款流程
sleep_time = random.randrange(1, 8)
time.sleep(sleep_time)
t = threading.current_thread()
print(f'{t.name}网点,已经售出第{self.ticket_count}张票')
self.ticket_count -= 1
db = TicketDB()
lock = threading.RLock()
def thread_body():
global db, lock
while True:
with lock:
curr_ticket_count = db.get_ticket_count()
if curr_ticket_count > 0:
db.sell_ticket()
else:
break
time.sleep(1)
def main():
t1 = threading.Thread(target=thread_body)
t1.start()
t2 = threading.Thread(target=thread_body)
t2.start()
if __name__ == '__main__':
main()
result:
Thread-1网点,已经售出第5张票
Thread-1网点,已经售出第4张票
Thread-1网点,已经售出第3张票
Thread-2网点,已经售出第2张票
Thread-2网点,已经售出第1张票