可以创建一个知道方法对象的@synchronized 装饰器吗?

Posted

技术标签:

【中文标题】可以创建一个知道方法对象的@synchronized 装饰器吗?【英文标题】:Possible to create a @synchronized decorator that's aware of a method's object? 【发布时间】:2015-06-06 19:08:18 【问题描述】:

我正在尝试创建一个 @synchronized 包装器,该包装器为每个对象创建一个 Lock 并使方法调用线程安全。如果我可以访问包装方法中的方法的method.im_self,我只能这样做。

    class B:
        def f(self): pass

    assert inspect.ismethod( B.f ) # OK
    assert inspect.ismethod( B().f ) # OK
    print B.f    # <unbound method B.f>
    print B().f  # <bound method B.f of <__main__.B instance at 0x7fa2055e67e8>>



    def synchronized(func):
        # func is not bound or unbound!
        print func  # <function f at 0x7fa20561b9b0>    !!!!

        assert inspect.ismethod(func)  # FAIL
        # ... allocate one lock per C instance
        return func

    class C:
        @synchronized
        def f(self): pass

(1) 令人困惑的是,传递给我的装饰器的 func 参数在传递给包装器生成器之前会更改类型。这似乎是粗鲁和不必要的。为什么会这样?

(2) 是否有一些装饰器魔术,我可以通过它对对象互斥锁进行方法调用(即每个对象一个锁,而不是每个类)。

更新:@synchronized(lock) 包装器有很多示例。然而,我真正想要的是@synchronized(self)。我可以这样解决:

    def synchronizedMethod(func):
        def _synchronized(*args, **kw):
             self = args[0]
             lock = oneLockPerObject(self)
             with lock: return func(*args, **kw)
        return _synchronized

但是,因为它更有效,我更喜欢:

    def synchronizedMethod(func):
        lock = oneLockPerObject(func.im_self)

        def _synchronized(*args, **kw):
             with lock: return func(*args, **kw)

        return _synchronized

这可能吗?

【问题讨论】:

【参考方案1】:

你不能在装饰时获得self,因为装饰器是在函数定义时应用的。尚无self;事实上,还不存在。

如果您愿意将您的锁存储在实例上(可以说是每个实例的值应该去的地方),那么这可能会做到:

def synchronized_method(func):
    def _synchronized(self, *args, **kw):
         if not hasattr(self, "_lock"): self._lock = oneLockPerObject(self)
         with self._lock: return func(self, *args, **kw)
    return _synchronized

您还可以在某种基类上的__init__() 方法中生成锁,并以相同的方式将其存储在实例中。这简化了您的装饰器,因为您不必检查 self._lock 属性是否存在。

【讨论】:

这是更好的答案,IMO。与通常假定全局锁的装饰器相比,它更安全,适合更多用例。 小错字,但应该是hasattr【参考方案2】:

去阅读:

https://github.com/GrahamDumpleton/wrapt/tree/develop/blog

特别是:

https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/07-the-missing-synchronized-decorator.md https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/08-the-synchronized-decorator-as-context-manager.md

wrapt 模块然后包含那里描述的@synchronized 装饰器。

https://pypi.python.org/pypi/wrapt

完整的实现足够灵活:

@synchronized # lock bound to function1
def function1():
    pass 

@synchronized # lock bound to function2
def function2():
    pass 

@synchronized # lock bound to Class
class Class(object):  

    @synchronized # lock bound to instance of Class
    def function_im(self):
        pass 

    @synchronized # lock bound to Class
    @classmethod
    def function_cm(cls):
        pass

    @synchronized # lock bound to function_sm
    @staticmethod
    def function_sm():
        pass

还有上下文管理器之类的用法:

class Object(object):  

    @synchronized
    def function_im_1(self):
        pass  

    def function_im_2(self):
        with synchronized(self):
            pass

更多信息和示例也可以在以下位置找到:

http://wrapt.readthedocs.org/en/latest/examples.html

您还可以在以下位置观看有关如何实施的会议演讲:

https://www.youtube.com/watch?v=EB6AH-85zfY&t=1s

【讨论】:

记得使用来自wrapt模块的decorator【参考方案3】:

(1) 令人困惑的是func参数传递给我的装饰器 在将类型传递到包装器生成器之前更改类型。这 似乎是粗鲁和不必要的。为什么会这样?

没有!相反,函数对象(和其他描述符)在调用它们的方法时会产生它们的__get__ 的结果——而那个结果就是方法对象!

但存在于class__dict__ 中的始终是描述符——特别是函数 对象!看看吧……:

>>> class X(object):
...   def x(self): pass
... 
>>> X.__dict__['x']
<function x at 0x10fe04e60>
>>> type(X.__dict__['x'])
<type 'function'>

看到了吗?到处都没有方法对象根本!-)

因此,在装修时也没有im_self——你需要采用基于内省的替代想法。

【讨论】:

谢谢——有帮助。为什么语言会从函数中生成未绑定的方法?为什么不总是有一个未绑定的方法?这似乎也阻止了每个类的同步。 @user48956,在 Python 3(从 3.0 开始)只有 bound 方法 -- unbound 方法已从语言中删除,所以只有 两个 类型,函数和绑定方法,可以更简单地完成这一切。也就是说,在 Python 2.0(15 年前)的远古时代,设计比必要的要复杂得多。但是,3.0 中的改进远比您建议的要简化得多,并且至少不会帮助您追求您喜欢的方法(即使您的装饰器的 arg 未绑定方法,它的im_self 将是None,所以,无论如何都没有帮助!-)。

以上是关于可以创建一个知道方法对象的@synchronized 装饰器吗?的主要内容,如果未能解决你的问题,请参考以下文章

Java中synchronized和Lock的区别

在Java中,当一个线程进入一个对象的一个synchronized方法后,其它线程是不是可进入此对象的其它方法?

synchronized修饰普通方法和静态方法

内置锁synchronized的几个要注意的对象监视器

Java中synchronized用在静态方法和非静态方法上面的区别

Java and C++