Python 继承运行不可重入关键节的方法
我有一个Python程序,它有多个线程来操作共享资源。我使用一个对象对该资源建模,并使用该对象中的方法对该资源执行操作。在一个关键部分,许多动作需要作为原子动作来执行。由于这些函数遵循一种常见模式,因此我使用了一个装饰器:Python 继承运行不可重入关键节的方法,python,decorator,Python,Decorator,我有一个Python程序,它有多个线程来操作共享资源。我使用一个对象对该资源建模,并使用该对象中的方法对该资源执行操作。在一个关键部分,许多动作需要作为原子动作来执行。由于这些函数遵循一种常见模式,因此我使用了一个装饰器: def critical_section(inner): def outer(self, *args, **kwargs): self.enter() inner(self, *args, **kwargs) self.
def critical_section(inner):
def outer(self, *args, **kwargs):
self.enter()
inner(self, *args, **kwargs)
self.leave()
return outer
真正的代码比这更复杂,例如,它处理异常,但这应该足以了解我正在做的事情的要点。示例用法(我简化了所有与线程相关的内容,只需检查资源所有权):
带有critical\u部分的函数具有两个属性:
- 函数的代码在资源的锁被保持的情况下执行(
enter
和leave
注意这一点)
- 该函数假定资源在进入时处于稳定状态,在退出时保持稳定状态
第二个属性意味着锁不是可重入的。在已经处于临界区的情况下调用“临界区”函数是无效的,因为不保证必要的不变量
enter
和leave
的实现检查以下属性:如果资源当前拥有(甚至由该线程本身拥有),线程不能调用enter
,并且enter
将资源的所有权授予调用线程<代码>离开
对称地要求所有权并放弃所有权
在我想拥有多个具有类似结构的资源之前,这种体系结构一直很好地为我服务,所以我开始使用继承
class Derived(Base):
@critical_section
def foo(self):
print('fie') # Additional stuff to run first
Base.foo(self)
现在我们来讨论这个问题:这个装饰者从根本上说是对继承不友好的
base = Base()
base.foo()
derived = Derived()
derived.foo()
调用derived.foo()
失败,因为当执行Base.foo()
时,资源已经拥有。在调用基类的方法时,派生类可能已经破坏了对象,违反了base.foo
以已知稳定状态的对象开始的假设
显而易见的解决方案是将每个关键部分函数转换为一对函数:外部函数(意味着从外部调用,并且在修改资源行为的类中从不重写),以及内部函数(意味着仅从派生方法调用)
编辑:这是一个正确的不可重入版本 您可以让装饰程序接受参数。十二月(x)日,;def f()…的调用方式类似于
dec(x)(f)(args)
。因此,我们有一个字符串(@critical\u section(“Base”)
),每个字符串有一个锁
def critical_section(ident):
def _critical_section(inner):
def outer(self, *args, **kwargs):
self.enter(ident)
inner(self, *args, **kwargs)
self.leave(ident)
return outer
return _critical_section
class Base:
def __init__(self):
self.owner = {}
def enter(self, ident):
assert(ident not in self.owner)
self.owner[ident] = True
def leave(self, ident):
assert(ident in self.owner)
del self.owner[ident]
@critical_section("Base")
def foo(self):
print('foo') # Dangerous stuff, must run atomically and not fail
class Derived(Base):
@critical_section("Derived")
def foo(self):
print('fie') # Additional stuff to run first
Base.foo(self)
你好,昨天的我。你的根本问题是你把情况简单化了。您将两件事混为一谈:进入/离开关键部分,以及假设/断言资源的不变量。但是资源的不变量也可以在临界区的中间是真的,这正是当你说“代码>派生”时你试图传达的。FoO 方法被允许调用<代码> Base.FoO (在执行过程中的某个特定点)。 您可以用Python对此进行建模,但它确实有点麻烦 def critical_section(inner): def outer(self, *args, **kwargs): self.enter() inner(self, *args, **kwargs) self.leave() return outer class Base: def __init__(self): self.owner = None self.invariants = True def enter(self): assert(self.invariants) self.invariants = False
回到您的问题,即在需要时访问内部函数,Python确实让它变得非常简单。将内部函数存储在修饰函数的属性中 def critical_section(inner): def outer(self, *args, **kwargs): self.enter() inner(self, *args, **kwargs) self.leave() outer.inner_function = inner return outer … class Derived(Base): @critical_section def foo(self): print('fie') # Additional stuff to run first Base.inner.foo(self) def临界_段(内部): def外部(自身、*args、**kwargs): self.enter() 内部(自我,*args,**kwargs) self.leave() outer.inner\u函数=inner 返回外部 … 派生类(基): @临界截面 def foo(self): 打印(“fie”)#要首先运行的其他内容 底部。内部。foo(自身)
正如我在问题中所解释的,当我从类内部调用
enter
时,这通常是一个错误,表明我依赖于当前无法保证的不变量。我最初使用的是threading.RLock
,后来改用它,因为它在我的资源实现中没有检测到编程错误。我试图得到“非可重入的,除非明确允许”,而不是
def critical_section(inner):
def outer(self, *args, **kwargs):
self.enter()
inner(self, *args, **kwargs)
self.leave()
outer.inner_function = inner
return outer
…
class Derived(Base):
@critical_section
def foo(self):
print('fie') # Additional stuff to run first
Base.inner.foo(self)