2

我正在寻找一种通用且简单的方法来同步本身不使用异步调用的python类的方法。我想到了一些可能性:首先,在类本身的所有方法上使用装饰器:http: //code.activestate.com/recipes/577105-synchronization-decorator-for-class-methods/。但我不希望更改类,所以其次,使用包装器或子类同步访问所有子类/核心方法。我想也许,有一种通用的方法来同步对 python 对象的访问,这样你就不会意外地错过层次结构中超类的方法(特别是如果它稍后被更改)。因此,第三,您可能可以使用类似于以下内容的通用代理:http://code.activestate.com/recipes/366254-generic-proxy-object-with-beforeafter-method-hooks/ 并为每次访问使用可重入锁。我更喜欢第三种选择。只是让我烦恼的是我没有找到这个食谱。这个解决方案有什么问题还是有更好的解决方案?

EDIT2: 最后一个选项类似于以下代码段,并已使用 codetidy.com/5911/ 进行了测试。测试不是证明它有效,只是一个轻微的指示。由于这不是我的日常编码,如果有经验的人可以检查是否有任何错误,这将有所帮助。

#!/usr/bin/env python
import types
from pprint import pformat
from threading import RLock

class SynchronizeMethodWrapper:
    """
    Wrapper object for a method to be called.
    """
    def __init__( self, obj, func, name, rlock ):
        self.obj, self.func, self.name = obj, func, name
        self.rlock = rlock
        assert obj is not None
        assert func is not None
        assert name is not None

    def __call__( self, *args, **kwds ):
        """
        This method gets called before a method is called to sync access to the core object.
        """
        with self.rlock:
            rval = self.func(*args, **kwds)
            return rval


class SynchronizeProxy(object):
    """
    Proxy object that synchronizes access to a core object methods and attributes that don't start with _.
    """
    def __init__( self, core ):
        self._obj = core
        self.rlock = RLock()

    def __getattribute__( self, name ):
        """
        Return a proxy wrapper object if this is a method call.
        """
        if name.startswith('_'):
            return object.__getattribute__(self, name)
        else:
            att = getattr(self._obj, name)
            if type(att) is types.MethodType:
                return SynchronizeMethodWrapper(self, att, name, object.__getattribute__(self, "rlock"))
            else:
                return att

    def __setitem__( self, key, value ):
        """
        Delegate [] syntax.
        """
        name = '__setitem__'
        with self.rlock:
            att = getattr(self._obj, name)
            pmeth = SynchronizeMethodWrapper(self, att, name, self.rlock)
            pmeth(key, value)

EDIT3:我使用了 SynchronizeProxy,到目前为止它似乎有效。由于此解决方案最接近我的需要,因此我将选择我的答案作为解决方案

4

3 回答 3

2

如果你真的需要,你可以使用 python 元类的黑魔法在类创建时为类的每个方法动态添加一个装饰器。以下是一个快速示例,说明您可以如何执行此操作。它创建了一个通用同步器元类,然后您将其子类化以为每个特定类创建同步器。最后,您将要同步的原始类子类化并将同步器元类应用于它。注意我使用的是 python 3 元类语法。

from threading import RLock

#
# Generic synchronizer
#
class SynchroMeta(type):

    def __init__(cls, name, bases, dct):
        super(SynchroMeta, cls).__init__(name, bases, dct)
        dct['__lock__'] = RLock()

        def sync_decorator(f):
            def inner(*args, **kwargs):
                with dct['__lock__']:
                    print("Synchronized call")
                    return f(*args, **kwargs)
            return inner

        for b in bases:
            if b.__name__ == cls.sync_object_name:
                for name, obj in b.__dict__.items():
                    # Synchronize any callables, but avoid special functions
                    if hasattr(obj, '__call__') and not name.startswith('__'):
                        print("Decorating: ", name)
                        setattr(b, name, sync_decorator(obj))

#
# Class you want to synchronize
#
class MyClass:
    def __init__(self, v):
        self.value = v

    def print_value(self):
        print("MyClass.value: ", self.value)

#
# Specific synchronizer for "MyClass" type
#
class MyClassSynchro(SynchroMeta):
    sync_object_name = "MyClass"


#
# Wrapper that uses the specific synchronizer metaclass
#
class MyClassWrapper(MyClass, metaclass=MyClassSynchro):
    pass


if __name__ == "__main__":
    w = MyClassWrapper('hello')
    w.print_value()
于 2013-06-11T14:09:55.040 回答
2

我选择了 SynchronizeProxy,到目前为止它似乎工作正常。由于此解决方案最接近我的需要,因此我将选择我的答案作为解决方案。如果我遇到任何问题,我会更新这个答案。

#!/usr/bin/env python
import types
from pprint import pformat
from threading import RLock

class SynchronizeMethodWrapper:
    """
    Wrapper object for a method to be called.
    """
    def __init__( self, obj, func, name, rlock ):
        self.obj, self.func, self.name = obj, func, name
        self.rlock = rlock
        assert obj is not None
        assert func is not None
        assert name is not None

    def __call__( self, *args, **kwds ):
        """
        This method gets called before a method is called to sync access to the core object.
        """
        with self.rlock:
            rval = self.func(*args, **kwds)
            return rval


class SynchronizeProxy(object):
    """
    Proxy object that synchronizes access to a core object methods and attributes that don't start with _.
    """
    def __init__( self, core ):
        self._obj = core
        self.rlock = RLock()

    def __getattribute__( self, name ):
        """
        Return a proxy wrapper object if this is a method call.
        """
        if name.startswith('_'):
            return object.__getattribute__(self, name)
        else:
            att = getattr(self._obj, name)
            if type(att) is types.MethodType:
                return SynchronizeMethodWrapper(self, att, name, object.__getattribute__(self, "rlock"))
            else:
                return att

    def __setitem__( self, key, value ):
        """
        Delegate [] syntax.
        """
        name = '__setitem__'
        with self.rlock:
            att = getattr(self._obj, name)
            pmeth = SynchronizeMethodWrapper(self, att, name, self.rlock)
            pmeth(key, value)
于 2013-07-05T18:17:09.590 回答
1

您可以使用队列来代理对该类的调用:

http://pymotw.com/2/Queue/

更新:

实际上,现在我认为队列可能不是最好的解决方案,因为您可能必须至少对类进行一些调整才能使用队列。如果你不想使用锁,你可以在这里查看 threading.Lock():http: //docs.python.org/2/library/threading.html

于 2013-06-11T11:10:28.950 回答