[英]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 A
和class C
如果C
使用B
,则会导致dedlock 。
我可以使用RLock
但它是无效的编程模式,不确定是否不会产生更严重的死锁。
如何在类初始化期间将sharedLock值更改为新锁,以使id(A.sharedLock) != id(B.sharedLock)
与A
和C
以及B
和C
?
如何在通用类中挂钩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.