同步对python对象的访问
我正在寻找一种通用且简单的方法来同步python类的方法,该类本身不使用异步调用。 我想到了一些可能性: 首先,在类本身的所有方法上使用decorator:。 但是我不想改变类,所以第二,使用包装器或子类同步访问所有子类/核心方法。 我想,也许有一种通用的方法可以同步对python对象的访问,这样您就不会意外地错过层次结构中超类的方法(特别是在以后发生更改时)。 因此,第三,您可能可以使用类似以下的通用代理: 并对每次访问使用可重入锁。 我更喜欢第三种选择。我没有找到这道菜的配方,这让我很恼火。这个解决方案有什么问题吗?还是有更好的解决方案 EDIT2: 最后一个选项类似于下面的代码段,并使用codetiy.com/5911/进行了测试。 测试不是证明它有效,只是一个轻微的指示。由于这不是我的日常编码,如果有更有经验的人可以检查一下是否有任何bug,这会有所帮助同步对python对象的访问,python,synchronization,Python,Synchronization,我正在寻找一种通用且简单的方法来同步python类的方法,该类本身不使用异步调用。 我想到了一些可能性: 首先,在类本身的所有方法上使用decorator:。 但是我不想改变类,所以第二,使用包装器或子类同步访问所有子类/核心方法。 我想,也许有一种通用的方法可以同步对python对象的访问,这样您就不会意外地错过层次结构中超类的方法(特别是在以后发生更改时)。 因此,第三,您可能可以使用类似以下的通用代理: 并对每次访问使用可重入锁。 我更喜欢第三种选择。我没有找到这道菜的配方,这让我很恼火。
#!/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,到目前为止它似乎还有效。由于此解决方案最接近我的需要,我将选择我的答案作为解决方案您可以使用队列代理对该类的调用: 更新:
事实上,现在我认为队列可能不是最好的解决方案,因为您可能至少需要对类进行一点调整才能使用队列。如果不想使用锁,可以在此处查看threading.Lock():如果确实需要,可以使用python元类的黑魔法在类创建时向类的每个方法动态添加一个修饰符。下面是一个快速示例,说明了如何执行此操作。它创建一个通用同步器元类,然后您可以将其子类化以为每个特定类创建同步器。最后,对要同步的原始类进行子类化,并对其应用同步器元类。注意,我使用的是Python3元类语法
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()
我用的是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)
您能否详细说明如何使用队列代理类的调用,以及它如何优于建议的解决方案?我已经对我的答案做了一些修改。希望有帮助。如果SynchrongMeta是一个元类,就我所理解的元类而言,它只有一个“实例”。因此,只有一个锁,它也会锁定MyClass的独立实例。这是可以的,但就我所见,没有比我的方法更好的方法,尤其是对于MyClass的许多对象。它不应该是一个可重入锁,因为有些方法可能调用同一类的另一个方法,从而导致死锁吗?是的,你是对的。我会更新的。这也是你的第一个评论。应该有一种方法可以添加每个实例的锁。我想我还需要重写元类的
\uuuuu call\uuuu
方法,并为每个创建的实例添加一个锁。仅供参考,我不是元类方面的大师,我只知道足够危险=)。这已经是一段很长的时间了,但更新仍然是必要的great@Magnus当时这个解决方案对我有效,我没有看到更好或更简单的替代方案。如今,随着在团队中工作经验的增加,我会努力寻找一种不那么优雅/更野蛮的解决方案来满足我的需求。取舍是要有一个有点难看的解决方案,但在理解/审查/调试/更改代码时,它的优点是可以让同事、未来的员工(以及我未来的自己)访问。