我有一个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
def enter(self):
assert(self.owner == None)
self.owner = True
def leave(self):
assert(self.owner == True)
self.owner = None
@critical_section
def foo(self):
print('foo') # Dangerous stuff, must run atomically and not fail
critical_section
装饰器的函数有两个属性:
enter
并leave
负责处理。)第二个属性意味着锁不是可重入的锁。在已经处于关键部分时调用“临界区”功能是无效的,因为不能保证必要的不变量。
enter
和leave
的实现检查这些属性:如果资源当前拥有(即使是该线程本身),线程也无法调用enter
,{{1将资源的所有权赋予调用线程; enter
对称地需要所有权并放弃。
这个架构很好地帮助了我,直到我想拥有具有类似结构的多个资源,所以我开始使用继承。
leave
现在我们遇到了问题:装饰器从根本上说是继承 - 不友好。
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()
以处于已知稳定状态的对象开始的假设。
显而易见的解决方案是将每个关键部分函数转换为一对函数:外部函数(意味着从外部调用,而不会在修改资源行为的类中重写)和内部函数(仅用派生方法调用)。
答案 0 :(得分:1)
编辑:这是一个正确的非重入版本。
你可以让装饰者接受参数。因此@dec(x); def f() ...
将被称为dec(x)(f)(args)
。所以,我们critical_section
接受一个字符串(@critical_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)
答案 1 :(得分:0)
你好,我是昨天来的。你的根本问题是你过分简化了这种情况。你混淆了两件事:进入/离开关键部分,以及假设/断言资源的不变量。但是,在关键部分的中间,资源的不变量也是正确的,当你说Derived.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 = Falseassert(self.owner == None)self.owner = True def leave(self): assert(self.owner == True) self.owner = None self.invariants = True @critical_section def foo(self): print('foo') # Dangerous stuff, must run atomically and not fail class Derived(Base): @critical_section def foo(self): print('fie') # Additional stuff to run first self.invariants = True Base.foo(self)
所有者的东西应该是现实世界中的折返锁。在不稳定的状态下,不存在不可重入以防止修改资源,而是检查这种不变量。
但是当“检查不变量”等于“我声明在代码中此时验证了有用的不变量”时,所有这些都很复杂并且不值得。通过静态分析检查不变量,这将是另一个故事,但是你不会使用Python。
回到你的问题,在需要时访问内部函数,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)