Python 继承运行不可重入关键节的方法

Python 继承运行不可重入关键节的方法,python,decorator,Python,Decorator,我有一个Python程序,它有多个线程来操作共享资源。我使用一个对象对该资源建模,并使用该对象中的方法对该资源执行操作。在一个关键部分,许多动作需要作为原子动作来执行。由于这些函数遵循一种常见模式,因此我使用了一个装饰器: def critical_section(inner): def outer(self, *args, **kwargs): self.enter() inner(self, *args, **kwargs) self.

我有一个Python程序,它有多个线程来操作共享资源。我使用一个对象对该资源建模,并使用该对象中的方法对该资源执行操作。在一个关键部分,许多动作需要作为原子动作来执行。由于这些函数遵循一种常见模式,因此我使用了一个装饰器:

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
以已知稳定状态的对象开始的假设

显而易见的解决方案是将每个关键部分函数转换为一对函数:外部函数(意味着从外部调用,并且在修改资源行为的类中从不重写),以及内部函数(意味着仅从派生方法调用)

  • Python decorators能帮助我用最少的样板文件定义这样的函数对吗
  • 是否有更好的体系结构可以最大限度地明确关键部分的进出位置,并将误用风险降至最低?(动态检查很好,但源代码的明显正确性更好。)

  • 编辑:这是一个正确的不可重入版本

    您可以让装饰程序接受参数。十二月(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 assert(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) def临界_段(内部): def外部(自身、*args、**kwargs): self.enter() 内部(自我,*args,**kwargs) self.leave() 返回外部 类库: 定义初始化(自): self.owner=无 self.invariants=True def输入(自我): 断言(自不变量) self.invariants=False 断言(self.owner==None) self.owner=True 假期(自我): 断言(self.owner==True) self.owner=无 self.invariants=True @临界截面 def foo(self): 打印(‘foo’)#危险的东西,必须以原子方式运行并且不能失败 派生类(基): @临界截面 def foo(self): 打印(“fie”)#要首先运行的其他内容 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) 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)