I want to create a synchronized method in python with respect to some CRUD functions on all instances of a class. For example while create is called and ongoing by a thread, delete needs to wait on the same object.
Can someone please tell me if the code below is correct. I may have some syntax error but what I want to know is if the lock is going to be the same across calls to create ALL instances of this class and therefore if any instance create/delete is in progress delete/create on the same or other instances by another thread will have to wait?
import threading
import functools
def synchronized(wrapped):
lock = threading.Lock()
@functools.wraps(wrapped)
def _wrap(*args, **kwargs):
with lock:
return wrapped(*args, **kwargs)
return _wrap
class AtomicCRUD(object):
@synchronized
def create(self):
#Do stuff that may take a while here.
pass
@synchronized
def delete(self):
#Do stuff that may take a while here.
pass
My understanding of python is synchronized will be called for each of create/delete function object. I put a print statement in the synchronized function above for the lock object and did a test run using the following:
@synchronized
def test1():
print "test1"
@synchronized
def test2():
print "test2"
And I get the following output, which makes me think the lock used is the same for both function objects. I don't seem to understand how this works.
<Semaphore c=1 _w[0]>
<Semaphore c=1 _w[0]>
test1
test2
The Java programming language provides two basic synchronization idioms: synchronized methods and synchronized statements.
Only one thread per instance can execute inside a synchronized instance method.
Synchronization in java is the capability to control the access of multiple threads to any shared resource. In the Multithreading concept, multiple threads try to access the shared resources at a time to produce inconsistent results. The synchronization is necessary for reliable communication between threads.
Your output prints the same <Semaphore c=1 _w[0]>
but it doesn't necessarily indicate that those are the same objects. It depends what your print
statement is.
To make sure you are not using the same Lock
object you could add a print
statement in the _wrap
function as follow:
def synchronized(wrapped):
lock = threading.Lock()
@functools.wraps(wrapped)
def _wrap(*args, **kwargs):
print "Calling '%s' with Lock %s" % (wrapped.__name__, id(lock))
with lock:
return wrapped(*args, **kwargs)
return _wrap
You will get a different id
printed every time you call create
or delete
:
AtomicCRUD().delete()
# Calling 'delete' with Lock 3075170560
AtomicCRUD().delete()
# Calling 'delete' with Lock 3075170560
AtomicCRUD().create()
# Calling 'create' with Lock 3075170544
AtomicCRUD().create()
# Calling 'create' with Lock 3075170544
The decorator synchronized
is only call twice - when the interpreter reads the decorated method declaration. The decorator "replace" the decorated method with the implementation of _wrap
but not with what is above functools.wraps(wrapped)
so the Lock
is only created once per decorated method.
Each decorated method has its own Lock
.
In the code above we can also see that this works for any instance of AtomicCRUD
since we re-instantiate an object every time but the result is the same when using a single instance
crud = AtomicCRUD()
crud.delete()
# Calling 'delete' with Lock 3075059968
crud.delete()
# Calling 'delete' with Lock 3075059968
crud.create()
# Calling 'create' with Lock 3075059952
crud.create()
# Calling 'create' with Lock 3075059952
And with a complete example we can see that the Lock
s behave as expected:
import threading
import functools
import time
def synchronized(wrapped):
lock = threading.Lock()
print lock, id(lock)
@functools.wraps(wrapped)
def _wrap(*args, **kwargs):
with lock:
print ("Calling '%s' with Lock %s from thread %s [%s]"
% (wrapped.__name__, id(lock),
threading.current_thread().name, time.time()))
result = wrapped(*args, **kwargs)
print ("Done '%s' with Lock %s from thread %s [%s]"
% (wrapped.__name__, id(lock),
threading.current_thread().name, time.time()))
return result
return _wrap
class AtomicCRUD(object):
@synchronized
def create(self):
#Do stuff that may take a while here.
time.sleep(1)
@synchronized
def delete(self):
#Do stuff that may take a while here.
time.sleep(1)
class SyncThread(threading.Thread):
def __init__(self, crud, name):
super(self.__class__, self).__init__(name=name)
self._crud = crud
def run(self):
self._crud.create()
self._crud.delete()
crud = AtomicCRUD()
threads = [SyncThread(crud, "Thread_%d" % i) for i in range(5)]
for t in threads:
t.start()
for t in threads:
t.join()
The output shows that create
cannot be called at the same time from different threads. But delete
and create
can be called at the same time from different thread.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With