Python如何判断当前线程是否持有锁

5 投票
4 回答
10775 浏览
提问于 2025-04-18 06:25

我有一个叫做 threading.Lock 的对象,我想知道当前的线程(也就是正在运行的程序部分)是否正在持有这个锁。有什么简单的方法可以做到这一点呢?

4 个回答

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

在Python 3.9.5中,如果你想知道一个RLock对象的拥有线程,可以这样做:

  • 如果是为了调试,你可以直接打印这个锁,输出的内容大概是这样的:

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

    你可以看看owner属性,它里面包含了拥有这个锁的线程的ID。

  • 如果想通过代码获取拥有者的值:(注意:这个方法依赖于具体的实现,未来版本可能会失效;另外要注意,在你检查拥有者和实际使用这个信息之间可能会有竞争条件)

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

    In [12]: threading._CRLock()  # this is a recursive lock implemented in C
    Out[12]: <unlocked _thread.RLock object owner=0 count=0 at 0x7f7746878cc0>
    
    In [13]: threading._PyRLock()  # this is a recursive lock implemented in Python
    Out[13]: <unlocked threading._RLock object owner=None count=0 at 0x7f7746764cd0>
    

    下面的方法只在实现是Python的时候有效。

    你只需要访问_owner属性:

    threading._PyRLock()._owner
    
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,那么这个锁仍然会被那个线程持有,其他线程就无法获取这个锁。

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

但实际上,你的代码其实不应该需要这样做。你为什么觉得需要这个明确的检查呢?通常在写一段代码时,你应该知道在这个上下文中锁是否已经被获取。你能分享一个你觉得需要检查的例子吗?

撰写回答