Reputation: 2751
In Python 3, I want to acquire a lock and then know whether it blocked or not. The problem is that threading.Lock.acquire
always returns True
if it is called with blocking=True
, so there is no way to tell whether the lock was already locked at the moment when the function was called. Take this code for example:
import threading
foo = None
lock = threading.Lock()
def bar():
global foo
# Only compute foo in one thread at a time.
if not lock.acquire(blocking=False):
# The race condition exists here.
# Another thread is already computing foo.
# This instance does not need to recompute foo.
# Wait for that instance to finish.
with lock:
# Just return the value that the other instance computed.
return foo
# No other instance of this function is computing foo.
with lock:
# Compute foo.
foo = [something]
return foo
The problem here is that lock
can be acquired again where a comment in the code above says that a race condition exists.
If this is because a third thread is at the same point in the function continued first and acquired the lock, this is undesirable because it introduces a slight delay. There really is no reason that return foo
needs to be protected; two threads should be able to do it at the same time.
However, if the acquire is due to another thread recomputing foo
, then this is undesirable because foo
will have changed once the lock is released. The function should return the value of foo
that was being computed when it was called. If foo
changes, then it cannot return that value anymore.
Ideally, we would have an acquire
function that can block and still return whether it blocked or not. That way, we can confidently assert that the function always returns the value of foo
that was being computed when the function was called and that only if foo
was not already being computed does the function then go ahead, compute it, and return the new value. Can this be done in Python?
Upvotes: 0
Views: 241
Reputation: 98
I know this question is old, but since I stumbled on it while looking for something else and it is unanswered, I figured I'd do anyone who finds it a service and answer it.
You are causing the race condition by first checking if a lock is available. You should try acquiring the lock without checking, like so:
import threading
foo = None
lock = threading.Lock()
def bar():
global foo
# Only compute foo in one thread at a time.
with lock:
# Only compute foo once.
if foo is None:
foo = [something]
# Just return the value that is now guaranteed to be computed.
return foo
Upvotes: 3