Class decoration takes our previous example one step further, and instead of protecting one single method, we can protect every function within our class so that all calls are done in a thread-safe manner.
In the following example, we look at how we can implement a class decorator function. The lock_class function at the start takes in a list of methods and lockfactory, and returns a lambda function which takes in the method names specified in the decorator as well as lockFactory.
This calls make_threadsafe, which initializes an instance of our passed-in class, it then defines a new constructor which also calls self._lock = lockfactory(). This make_threadsafe function then iterates through all of the methods in methodnames, and locks each method using the lock_method function.
This represents a clean and easy way to add thread safety to an entire class while also giving us the option to choose which functions we wish to lock:
from threading import Lock
def lock_class(methodnames, lockfactory):
return lambda cls: make_threadsafe(cls,
methodnames, lockfactory)
def lock_method(method):
if getattr(method, '__is_locked', False):
raise TypeError("Method %r is already locked!" %method)
def locked_method(self, *arg, **kwarg):
with self._lock:
return method(self, *arg, **kwarg)
locked_method.__name__ = '%s(%s)' %
('lock_method', method.__name__)
locked_method.__is_locked = True
return locked_method
def make_threadsafe(cls, methodnames,
lockfactory):
init = cls.__init__
def newinit(self, *arg, **kwarg):
init(self, *arg, **kwarg)
self._lock = lockfactory()
cls.__init__ = newinit
for methodname in methodnames:
oldmethod = getattr(cls, methodname)
newmethod = lock_method(oldmethod)
setattr(cls, methodname, newmethod)
return cls
@lock_class(['add','remove'], Lock)
class ClassDecoratorLockedSet(set):
@lock_method # if you double-lock a method, a TypeError is raised
def lockedMethod(self):
print("This section of our code would be thread safe")