Python如何知道当前线程是否持有锁

4

我有一个 threading.Lock 对象,我想知道当前线程是否持有这个锁。最简单的方法是什么?


1
你想要实现什么?你可能需要使用一个可重入锁。请参考https://dev59.com/kGAh5IYBdhLWcg3wDfkW了解更多信息。 - Rob Watts
如果是为了调试,也可以将Lock对象包装起来以保留信息。请参见multithreading - Is it possible to subclass Lock() objects in Python? If not, other ways to debug deadlock? - Stack Overflow - user202729
4个回答

7

我不知道有没有直接使用 threading.Lock 对象实现此目的的方法。虽然这些对象具有 locked 属性,但该属性在所有线程中都会显示为 True,而不仅仅是拥有者线程。如果使用 RLock,那么可以完成此操作,但必须访问 RLock 对象上的内部 __owner 属性,这并不可取且不能保证始终有效。以下是实现此目的的代码:

#!/usr/bin/python

import threading
import time

def worker():
    if l._RLock__owner is threading.current_thread():
        print "I own the lock"
    else:
        print "I don't own the lock"
    l.acquire()
    if l._RLock__owner is threading.current_thread():
        print "Now I own the lock"
    else:
        print "Now I don't own the lock"
    time.sleep(5)
    l.release()

if __name__ == "__main__":
    l = threading.RLock()
    thds = []
    for i in range(0, 2): 
        thds.append(threading.Thread(target=worker))
        thds[i].start()

    for t in thds:
        t.join()

以下是输出结果:

dan@dantop:~> ./test.py
I don't own the lock
Now I own the lock
I don't own the lock

但实际上,你的代码真的不应该这样做。为什么你觉得需要进行这种显式检查呢?通常情况下,在编写代码块时,您知道在该上下文中是否已获取锁定。你能分享一个例子,让我们看看你认为需要检查它吗?


注意:上述使用 RLock 的方法在 Python 3 中不起作用(实际上,因为它使用了一个未记录的特性,所以它可能会在任何新版本中出现问题)。 - user202729

2

release方法的帮助文本:

Help on built-in function release:

release(...)
    release()

    Release the lock, allowing another thread that is blocked waiting for
    the lock to acquire the lock.  The lock must be in the locked state,
    but it needn't be locked by the same thread that unlocks it.

这意味着Lock对象不在乎是哪个线程锁定了它们,因为任何线程都可以解锁它们。因此,对于未经修改的锁对象,没有办法确定当前线程是否持有锁,因为没有线程“持有”锁 - 它只是已被锁定或未被锁定。
我假设你想知道自己是否持有锁,这样就不会在已经持有锁时再次尝试获取它。例如:
def a():
    with theLock:
        do_stuff()
        b()
        do_other_stuff()

def b():
    with theLock:
        do_b_stuff()

在这里,你只想在b()中获取theLock,如果线程还没有持有锁。就像我在评论中提到的那样,这是重入锁的一个完美用例。如果你像这样创建锁:

theLock = threading.RLock()

然后,我所提供的示例可以正常工作 - 当您调用 a() 时,它会获取锁。然后在 b() 中,它允许您重新获取锁而不会有任何问题。此外,如果您使用上下文管理器语法 (with theLock:),则无需担心其中的一个注意事项。
注意事项是,如果您手动调用了acquirerelease,则需要确保每次调用acquire都要相应地调用一次release。 在上面的示例中,如果我在a()b()中都调用了theLock.acquire(),那么我也需要在两个函数中调用release()。 如果您调用acquire两次,但只调用release一次,则该线程仍将持有该锁,使其他线程不能获取该锁。

检查这个的一个很好的理由是为了函数断言在调用之前其调用者已经获取了锁。 - Brent

0
请查看线程同步机制
你可以尝试以下方法。
import threading
lock = threading.Lock()
lock.acquire()

#now  that the lock is taken, using lock.acquire(False) will return False
if not lock.acquire(False):
    # could not lock the resource

现在给这个加点变化:

import threading

class customLock(object):
    _accessLock = threading.Lock()
    def __init__(self):
        self._lock = threading.Lock()
        self.ownerThread = None

    def acquire(self, thread):
        self._accessLock.acquire()
        try:
            if self._lock.acquire():
                self.ownerThread = thread
        finally:
            self._accessLock.release()

    def release(self):
        self._accessLock.acquire()
        try:
            self._lock.release()
            self.ownerThread = None
        finally:
            self._accessLock.release()



    def getOwner(self):
        return self.ownerThread

    def acquire(self, blocking=True): 
        return self._lock.acquire(blocking)

将初始示例转换为:

import threading
lock = customLock()
lock.acquire(threading.current_thread())


#now  that the lock is taken, using lock.acquire(False) will return False
if not lock.acquire(False):
    # could not lock the resource
    if lock.getOwner() is threading.current_thread():
        # do something

希望这可以帮到你。

1
嗨,drapal,我喜欢这种方法,我唯一担心的是def release(self):步骤1 self._lock.release() 步骤2 self.ownerThread = None 步骤1和2不是原子性的,这会导致ownerThread即使有人已经获取了锁也变成None,你怎么看? - Rahul
这可能是个问题,我已经添加了一个内部锁来修复它。 - drapal

0
在 Python 3.9.5 中,要获取 RLock 对象的所有者线程:
  • 调试目的:您可以简单地打印锁定状态,输出将类似于

    <unlocked _thread.RLock object owner=0 count=0 at 0x7f77467d1030>
    

    查看owner属性,其中包含所有者线程的线程ID。

  • 以编程方式获取所有者的值:警告:使用实现细节,可能在未来版本中会出现问题;还要注意,在检查所有者和实际使用信息之间可能存在竞争条件

    这取决于Python是使用Python实现还是C实现。

    In [12]: threading._CRLock()  # 这是一个在C中实现的递归锁
    Out[12]: <unlocked _thread.RLock object owner=0 count=0 at 0x7f7746878cc0>
    
    In [13]: threading._PyRLock()  # 这是一个在Python中实现的递归锁
    Out[13]: <unlocked threading._RLock object owner=None count=0 at 0x7f7746764cd0>
    

    以下方法仅在实现为Python时有效。

    只需访问_owner属性:

    threading._PyRLock()._owner
    

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