I needed to impose on a Python method the following locking semantics: the method can only be run by one thread at a time. If thread B tries to run the method while it is already being run by thread A, then thread B immediately receives a return value of None
.
I wrote the following decorator to apply these semantics:
from threading import Lock
def non_blocking_lock(fn):
fn.lock = Lock()
@wraps(fn)
def locker(*args, **kwargs):
if fn.lock.acquire(False):
try:
return fn(*args, **kwargs)
finally:
fn.lock.release()
return locker
This works in my testing so far. Does anyone notice any gotchas or have any suggestions for improvements?
Revised version
After the suggestions by @RemcoGerlich, I have added a docstring and kept the lock local to the decorator:
from threading import Lock
def non_blocking_lock(fn):
"""Decorator. Prevents the function from being called multiple times simultaneously.
If thread A is executing the function and thread B attempts to call the
function, thread B will immediately receive a return value of None instead.
"""
lock = Lock()
@wraps(fn)
def locker(*args, **kwargs):
if lock.acquire(False):
try:
return fn(*args, **kwargs)
finally:
lock.release()
return locker
1 Answer 1
I think this will work fine and it's very close to how I would write it myself.
A few small things come to mind:
There's no documentation of any kind that explains what the semantics are, and they're not explicit either (the return None if the lock isn't acquired is entirely implicit). I would put the short explanation you put in this question into a docstring, and/or add an explicit
else: return None
to the if statement.is there any reason why the lock object is exposed to the outside world by making it a property of the function (
fn.lock
) ? I would simply make it a local variable, so that it's hidden. But I'm not sure.
-
\$\begingroup\$ Thank you for your feedback! I’ve amended my original code and added it to the question, for posterity. \$\endgroup\$bdesham– bdesham2014年02月26日 14:49:52 +00:00Commented Feb 26, 2014 at 14:49