I've written a class that I'm using as a decorator. What I've done clearly isn't thread safe, but needs to be.
Here's a very simple example:
class DecoratorClass(object):
def __init__(self):
print 'initing DecoratorClass'
self.counter = 0
def __call__(self, func, *args, **kwargs):
def wrapped_func(*args, **kwargs):
print '\nin wrapped func, func name:', func.func_name
print 'count val:', self.counter
self.counter += 1
ret_val = func()
return ret_val
return wrapped_func
@DecoratorClass()
def decorated_with_class():
return
decorated_with_class()
decorated_with_class()
decorated_with_class()
This is the output:
initing DecoratorClass
in wrapped func, func name: decorated_with_class
count val: 0
in wrapped func, func name: decorated_with_class
count val: 1
in wrapped func, func name: decorated_with_class
count val: 2
So I'm getting one instance of DecoratorClass, which is shared between the three functions that have been decorated with it. Is there a straightforward way to get a new DecoratorClass instance on each call of decorated_with_class()? Or some other way to make this thread safe (e.g. have self.counter begin at 0 each time)? Or is this a lost cause?
Edits for clarifiation:
I've intentionally omitted any threading in this example for the sake of keeping things simple; the environment in which I'm working WILL be threaded, but this example is not.
I'm not actually counting anything in my production code. My real issue is that I have an instance variable on my decorator class, which may be updated by two instances of the same function each running in different threads. I just used the counter example to show that the decorator class only gets one instance, which maintains state. I need each of the threads/functions to either 1. get their own instance of the decorator class or 2. wait until no other threads are using the decorator class.
Ok, here's a better example of what I was running into:
import threading
import time
class DecoratorClass(object):
def __init__(self):
self.thread = None
def __call__(self, func, *args, **kwargs):
def wrapped_func(*args, **kwargs):
curr_thread = threading.currentThread().getName()
self.thread = curr_thread
print '\nthread name before running func:', self.thread
ret_val = func()
print '\nthread name after running func:', self.thread
return ret_val
return wrapped_func
@DecoratorClass()
def decorated_with_class():
print 'running decorated w class'
time.sleep(1)
return
threads = []
for i in range(5):
t = threading.Thread(target=decorated_with_class)
threads.append(t)
t.start()
This outputs:
thread name before running func: Thread-1
running decorated w class
thread name before running func: Thread-2
thread name before running func: running decorated w classThread-3
thread name before running func:
running decorated w class
thread name before running func:Thread-5
running decorated w class
Thread-5
running decorated w class
thread name after running func: Thread-5
thread name after running func: Thread-5
thread name after running func: Thread-5
thread name after running func: Thread-5
thread name after running func: Thread-5
So, clearly all the threads are using the same instance of DecoratorClass, and self.thread ends up being Thread-5 by the time all of them are done running (rather than being the name of the thread that actually ran the code.)
I just needed to add a lock into my decorator, like so:
class DecoratorClass(object):
def __init__(self):
self.thread = None
self.lock = threading.Lock()
def __call__(self, func, *args, **kwargs):
def wrapped_func(*args, **kwargs):
self.lock.acquire()
curr_thread = threading.currentThread().getName()
self.thread = curr_thread
print '\nthread name before running func:', self.thread
ret_val = func()
print '\nthread name after running func:', self.thread
self.lock.release()
return ret_val
return wrapped_func
@DecoratorClass()
def decorated_with_class():
print 'running decorated w class'
time.sleep(1)
return
threads = []
for i in range(5):
t = threading.Thread(target=decorated_with_class)
threads.append(t)
t.start()
Now my output looks like this, which is what I want:
thread name before running func: Thread-1
running decorated w class
thread name after running func: Thread-1
thread name before running func: Thread-2
running decorated w class
thread name after running func: Thread-2
thread name before running func: Thread-3
running decorated w class
thread name after running func: Thread-3
thread name before running func: Thread-4
running decorated w class
thread name after running func: Thread-4
thread name before running func: Thread-5
running decorated w class
thread name after running func: Thread-5
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