繁体   English   中英

如何在Python中使用锁,继承和线程初始化类属性来避免死锁?

[英]How to avoid deadlock with class attributes initialization with locks, inheritance and threading in Python?

我正在尝试实现线程安全代码,但是遇到一些简单的问题。 我搜索并没有找到解决方案。

让我显示抽象代码来描述问题:

import threading

class A(object):
  sharedLock = threading.Lock()
  shared = 0

  @classmethod
  def getIncremented(cls):
    with cls.sharedLock:
      cls.shared += 1
      return cls.shared

class B(A):
  pass

class C(A):
  @classmethod
  def getIncremented(cls):
    with cls.sharedLock:
      cls.shared += B.getIncremented()
      return cls.shared

我想定义类A来继承许多子类,例如枚举或惰性变量-无论如何都可以使用。 我已经做完单线程版本,现在想更新多线程。

此代码将给出如下结果:

id(A.sharedLock) 11694384
id(B.sharedLock) 11694384
id(C.sharedLock) 11694384

我的意思是, class A的锁定是class B锁定,所以这很不好,因为第一次进入class B也会锁定class Aclass C 如果C使用B ,则会导致dedlock

我可以使用RLock但它是无效的编程模式,不确定是否不会产生更严重的死锁。

如何在类初始化期间将sharedLock值更改为新锁,以使id(A.sharedLock) != id(B.sharedLock)AC以及BC

如何在通用类中挂钩python中的类初始化以更改某些类变量?

这个问题不太复杂,但我不知道该怎么办。

我要继承共享 共享锁以外的父共享变量

您一定不能这样做。 它使对“共享变量”的访问不是线程安全的。


sharedLock保护shared变量。 如果可以在递归调用中修改相同的shared变量,则需要RLock() 在这里, shared是指所有子类之间共享。

看起来您想要一个独立的函数(或静态方法)而不是classmethod:

def getIncremented(_lock=Lock(), _shared=[0]):
    with _lock:
      _shared[0] += 1
      return _shared[0]

因此,所有类都使用相同的shared变量(和相应的lock )。

如果您希望每个类都有其自己的shared变量(此处的shared表示该特定类的实例之间共享),则不要使用cls.shared来遍历祖先。

为了暗示子类不应该直接使用变量,可以对私有变量使用语法:

class A:
   __shared = 0
   __lock = Lock()

如果子类覆盖使用__shared的方法,则它不会直接在代码中偶然使用A.__shared

如您所注意到的,如果将共享锁公开为类属性,则这些锁由子类共享。

您可以通过重新定义每个子类的锁来解决此问题:

class B(A):
  sharedLock = threading.Lock()

您甚至可以使用元类来实现这一点(请不要这样做)。 在我看来,您是从错误的角度来接近程序。

如果将锁显式分配给实例 (而不是类),则此任务会更容易。

class A(object):
    def __init__(self, lock):
        this.sharedLock= lock

my_lock= threading.Lock()
a= A(my_lock)

当然,您遇到了“必须为每个实例显式传递锁”的“问题”。 传统上,这是使用工厂模式解决的,但是在python中,您可以简单地正确使用函数:

from functools import partial
A_with_mylock= partial(A, my_lock)
a2= A_with_mylock()

这是解决方案-由于它是在类构造函数级别(metaclass)上完成的,因此可以为每个类单独锁定。 感谢您提供所有提示,并帮助实现此代码,它看起来非常不错。

我也可以使用变量变量,但需要使用硬编码“ _A__lock”,这可能会引起问题并且无法通过我进行测试。

import threading

class MetaA(type):
  def __new__(self, name, bases, clsDict):
    # change <type> behavior
    clsDict['_lock'] = threading.Lock()
    return super(MetaA, self).__new__(self, name, bases, clsDict)

class A(object):
  __metaclass__ = MetaA

  @classmethod
  def getLock(cls):
    return cls._lock

class B(A):
  pass

print 'id(A.getLock())', id(A.getLock())
print 'id(B.getLock())', id(B.getLock())
print A.getLock() == B.getLock()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM