为了确保对共享资源的访问,python提供了两种锁,一个是上一篇提到的Lock,还有一个就是RLock,他们的区别在于:
Lock是可用的最低级别的同步指令,一个线程只能请求一次,而RLock是可以被一个线程请求多次的同步指令
当Lock处于锁定状态时,不被特定的线程所拥有,而RLock使用了“拥有的线程”和“递归等级”的概念,因此处于锁定状态时,可以被线程拥有
Lock在下面的情形下会发生死锁
Lock.acquire()
Lock.acquire()
Lock.release()
Lock.release()
连续两次acquire请求,会导致死锁,因为第一次获得锁之后还没有释放时,第二次acquire请求紧接着就到来,可是acquire会让程序阻塞,无法执行release(),这就导致锁永远无法释放,死锁是非常危险非常严重的问题
RLock就不存在1.2中所提到的死锁问题
RLock.acquire()
RLock.acquire()
RLock.release()
RLock.release()
不过要保证有多少次acquire(),就有多少次release()
最初接触到Lock和RLock这两者之间的不同之处时,感到十分困惑。RLock的优势在于,在同一个线程里可以多次申请锁,而Lock则不能,必须在释放之后才能再次申请,那么,这样做也没问题啊,不会出现第一次申请后,在释放前又申请的可能啊,在编写代码的时候,完全可以认为的控制这种情况的发生。
然而事实并非如此,我现在假设一种情形,使得死锁的发生不可避免
import threading
m_lock = threading.Lock()
def h():
with m_lock:
print('h')
def g():
with m_lock:
print('g')
h()
g()
上面的例子中,h()和g()中都用了Lock,在多线程环境下,他们可以做到相安无事,但是,程序的结构总是处于变化中,尤其是那些庞大的系统,一个小小的变化可能牵一发而动全身,假设发生了下面的变化
import threading
m_lock = threading.Lock()
# m_lock = threading.RLock()
def h():
with m_lock:
g()
print('h')
def g():
with m_lock:
print('g')
h()
g()
在h()函数中,获得锁以后要执行g(),那么此时,程序就会发生死锁,在大的项目里,情况会比这更加复杂,你很难通过眼前的几行代码发现这种死锁的情况,因为很可能发生死锁的地方是在很深层次的调用过程中,因此,使用RLock是非常安全的选择.
执行上面的代码,程序不会输出任何信息,也永远不会结束,因为已经发生了死锁,将注释的Lock替换成RLock,程序立马可以执行
QQ交流群: 211426309