Reputation: 199
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?
Upvotes: 5
Views: 11534
Reputation: 3935
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
Upvotes: 1
Reputation: 1
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
Upvotes: 0
Reputation: 7146
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.
Upvotes: 2
Reputation: 94871
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?
Upvotes: 7