python多线程Lock和RLock的区别

1. 两种锁的不同

1.1 定义

为了确保对共享资源的访问,python提供了两种锁,一个是上一篇提到的Lock,还有一个就是RLock,他们的区别在于:

  1. Lock是可用的最低级别的同步指令,一个线程只能请求一次,而RLock是可以被一个线程请求多次的同步指令

  2. 当Lock处于锁定状态时,不被特定的线程所拥有,而RLock使用了“拥有的线程”和“递归等级”的概念,因此处于锁定状态时,可以被线程拥有

    1.2 死锁

    Lock在下面的情形下会发生死锁

    Lock.acquire()
    Lock.acquire()
    Lock.release()
    Lock.release()

    连续两次acquire请求,会导致死锁,因为第一次获得锁之后还没有释放时,第二次acquire请求紧接着就到来,可是acquire会让程序阻塞,无法执行release(),这就导致锁永远无法释放,死锁是非常危险非常严重的问题

1.3 可重入锁

RLock就不存在1.2中所提到的死锁问题

RLock.acquire()
RLock.acquire()
RLock.release()
RLock.release()

不过要保证有多少次acquire(),就有多少次release()

2. 怎么会多次请求锁呢?

最初接触到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

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案