Question

I have a threading.Lock object and I would like to know if the current_thread is holding this lock. What would be the simplest way of achieving this?

Was it helpful?

Solution

There is no direct way to do this with threading.Lock objects that I know of. Those do have a locked attribute, but that will show up as True in all threads, not just the owning thread. It's possible with RLock, but you have to access an internal __owner attribute on the RLock object, which isn't advisable or guaranteed to always work. The code to do is would look like this, for what its worth:

#!/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()

Here's the output:

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

But in reality, your code really shouldn't need to do this. Why do you feel like you need this explicit check? Typically when you're writing a block of code, you know whether or not a lock has been acquired in that context. Can you share an example where you think you need to check it?

OTHER TIPS

The help text for the release method:

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.

This implies that Lock objects don't care who locked them, as any thread can unlock them. So with unmodified lock objects, there is no way to determine if the current thread is the one holding the lock because no thread "holds" the lock - it is simply either locked or unlocked.

I assume that the reason you would want to know if you are holding the lock or not is so that you don't try to acquire the lock when you already have it. For example:

def a():
    with theLock:
        do_stuff()
        b()
        do_other_stuff()

def b():
    with theLock:
        do_b_stuff()

Here, you only want to acquire theLock in b() if the thread does not already have the lock. This, as I mentioned in my comment, is a perfect use case for a reentrant lock. If you create the lock like this:

theLock = threading.RLock()

Then the example I presented works just fine - when you call a(), it acquires the lock. Then in b(), it lets you reacquire the lock without complaining. Additionally, if you are using the context manager syntax (with theLock:) you won't need to worry about the one caveat.

The caveat is that if you are manually calling acquire and release, then you need to make sure that you call release once for every time you call acquire. In my above example, if I had called theLock.acquire() in both a() and b(), I would have needed to call release() in both functions as well. If you call acquire twice and release only once, then the lock will still be held by that thread, making other threads unable to acquire the lock.

Please take a look at Thread Synchronization Mechanisms

You could try the following

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

Now adding a twist to this:

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)

Turning the initial example into:

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

Hope this helps

In Python 3.9.5, to get the owner thread of a RLock object:

  • For debugging purpose: You can simply print the lock out, and the output would be something like

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

    Look at the owner property, which contains the thread ID of the owner thread.

  • To get the value of the owner programmatically: (warning: uses implementation details, might break in future versions;; also note that there may be a race condition between when you check the owner and actually use the information)

    It depends on whether Python is using the Python implementation, or the C implementation.

    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>
    

    The following method only works if the implementation is in Python.

    Just access the _owner attribute:

    threading._PyRLock()._owner
    
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top