创建线程安全装饰器类

时间:2016-01-05 01:56:34

标签: python multithreading decorator

我写了一个我用作装饰的课程。我清楚地做的不是线程安全,而是需要。

这是一个非常简单的例子:

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()

这是输出:

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

所以我得到一个DecoratorClass实例,它在用它装饰的三个函数之间共享。有没有一种直接的方法在每次调用decorated_with_class()时获取一个新的DecoratorClass实例?或者其他一些使这个线程安全的方法(例如,让self.counter每次从0开始)?或者这是一个失败的原因?

编辑澄清:

  • 为了简单起见,我故意省略了本例中的任何线程;我正在工作的环境将被线程化,但这个例子不是。

  • 我实际上并没有计算生产代码中的任何内容。我真正的问题是我的装饰器类上有一个实例变量,它可以由两个在不同线程中运行的相同函数的实例更新。我只是使用计数器示例来表明装饰器类只获得一个实例,它保持状态。我需要每个线程/函数1.获取自己的装饰器类的实例或2.等待其他线程没有使用装饰器类。

4 个答案:

答案 0 :(得分:1)

好的,这是我遇到的更好的例子:

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()

输出:

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

因此,显然所有线程都使用相同的DecoratorClass实例,并且self.thread在所有线程完成运行时最终成为Thread-5(而不是实际运行代码的线程的名称) 。)

我只需要在我的装饰器中添加一个锁,就像这样:

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()

现在我的输出看起来像这样,这就是我想要的:

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

答案 1 :(得分:0)

我不清楚为什么你需要DecoratorClass的多个实例,但是如果你刚刚将threading.Lock添加到包装的函数线程安全中呢?

class DecoratorClass(object):
    def __init__(self):
        print 'initing DecoratorClass'
        self.counter = 0
        self._lock = Lock()

    def __call__(self, func, *args, **kwargs):
        def wrapped_func(*args, **kwargs):
            with self._lock:
                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

答案 2 :(得分:0)

如果您有兴趣为每个装饰的功能维护计数器的副本,可以将其存储在功能本身中。请记住,函数是普通的Python对象:

D替换为

K

由于您的装饰器类将不再具有状态,您可以仅使用实现self.counter += 1的函数替换它:

if hasattr(func, "counter"):
    func.counter += 1
else:
    func.counter = 0

答案 3 :(得分:0)

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 decorated1():
    return

@DecoratorClass()
def decorated2():
    return

@DecoratorClass()
def decorated3():
    return

decorated1()
decorated2()
decorated3()

让我

initing DecoratorClass
initing DecoratorClass
initing DecoratorClass

in wrapped func, func name: decorated1
count val: 0

in wrapped func, func name: decorated2
count val: 0

in wrapped func, func name: decorated3
count val: 0

这有帮助吗?