Lock和RLock之间有什么区别?

118

来自 文档:

threading.RLock() -- 一个工厂函数,返回一个新的可重入锁对象。可重入锁必须由获取它的线程释放。一旦一个线程获取了可重入锁,同一个线程可以再次获取它而不会阻塞;但是线程每次获取锁后必须释放相同次数的锁。

我不确定我们为什么需要这个? RlockLock 有什么区别?

3个回答

176

主要区别在于Lock只能被获取一次,必须释放后才能重新获取(任何线程都可重新获取)。

RLock可以被同一个线程多次获取,需要同样多次的释放才能被“解锁”。

另一个区别是已获取的Lock可以被任何线程释放,而已获取的RLock只能由获取它的线程释放。


以下示例演示了为什么有时使用RLock很有用。假设你有:

def f():
  g()
  h()

def g():
  h()
  do_something1()

def h():
  do_something2()

假设 fgh 都是 公共 的(即可以直接被外部调用者调用),并且它们都需要同步。

使用一个 Lock,你可以这样做:

lock = Lock()

def f():
  with lock:
    _g()
    _h()

def g():
  with lock:
    _g()

def _g():
  _h()
  do_something1()

def h():
  with lock:
    _h()

def _h():
  do_something2()

基本上,由于 f 在获取锁之后无法调用 g,因此它需要调用“原始”版本的 g(即 _g)。因此,您最终会得到每个函数的“同步”版本和“原始”版本。

优雅地使用 RLock 解决了这个问题:

lock = RLock()

def f():
  with lock:
    g()
    h()

def g():
  with lock:
    h()
    do_something1()

def h():
  with lock:
    do_something2()

4
使用“Lock”有实际的“好处”吗? - Mateen Ulhaq
13
@MateenUlhaq,是的。 Lock 操作更快。 - shx2
4
抱歉如果这是一个愚蠢的问题,但我为什么要多次获取锁呢?如果我需要增加计数器,我只需获取一次锁并释放它即可,为什么需要在锁上保持一个计数器来不断地增加或减少呢? - PirateApp
@ErikAronesty 你是通过绕过锁来避免死锁的吗? - Torsten Bronger
@TorstenBronger,rlock 允许同一线程重新进入函数,这通常不是您需要保护的内容。特别是如果锁正在保护操作系统资源、SQLite 句柄等。 - Erik Aronesty
显示剩余3条评论

28

为了进一步解释shx2的回答,使用LockRLock的原因可能是以下两个方面:

普通的Lock(互斥锁)通常更快、更安全。

使用RLock的原因是为了避免由于递归导致死锁。例如,在递归阶乘函数中放置一个锁。(尽管有些牵强)

from threading import Lock

lock = Lock()

def factorial(n):
    assert n > 0
    if n == 1:
        return 1
    
    with lock:       
        out = n * factorial(n - 1)

    return out

如果使用这个函数会因为递归调用而导致死锁。如果我们使用 RLock 代替,递归调用就可以重新进入相同的锁,需要多少次都可以。因此称之为 可重入 (或 递归) 锁。


如果 n 等于 1: 返回 1 - Jamaica-Jan
你是对的,我已经更新了逻辑以从非递归分支开始。 - Kris

-4

RLock被称为可重入(递归)锁。基本上它是只有持有者才能释放的锁。在Lock中,任何线程都可以释放。


3
在计算机科学中,可重入互斥锁(又称递归锁)是一种特殊类型的互斥(mutex)设备,允许同一个进程/线程多次对其进行加锁,而不会导致死锁。 - Alperen Sözer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接