Python 实例变量是线程安全的吗?
Posted
技术标签:
【中文标题】Python 实例变量是线程安全的吗?【英文标题】:Are Python instance variables thread-safe? 【发布时间】:2012-01-08 17:48:28 【问题描述】:好的,先检查以下代码:
class DemoClass():
def __init__(self):
#### I really want to know if self.Counter is thread-safe.
self.Counter = 0
def Increase(self):
self.Counter = self.Counter + 1
def Decrease(self):
self.Counter = self.Counter - 1
def DoThis(self):
while True:
Do something
if A happens:
self.Increase()
else:
self.Decrease()
time.sleep(randomSecs)
def DoThat(self):
while True:
Do other things
if B happens:
self.Increase()
else:
self.Decrease()
time.sleep(randomSecs)
def ThreadSafeOrNot(self):
InterestingThreadA = threading.Thread(target = self.DoThis, args = ())
InterestingThreadA.start()
InterestingThreadB = threading.Thread(target = self.DoThat, args = ())
InterestingThreadB.start()
我面临与上述相同的情况。我真的很想知道self.Counter
是否是线程安全的,如果不是,我有什么选择?我只能想到threading.RLock()
锁定这个资源,有更好的办法吗?
【问题讨论】:
【参考方案1】:您可以使用锁、RLock、信号量、条件、事件和队列。 这篇文章对我的帮助很多。 看看吧:Laurent Luce's Blog
【讨论】:
【参考方案2】:使用实例字段self.Counter
是thread safe or "atomic"。读取它或分配一个 single 值 - 即使它在内存中需要 4 个字节,您也永远不会得到一半更改的值。但是操作self.Counter = self.Counter + 1
并不是因为它读取值然后写入它——另一个线程可以在读取之后和写回之前更改字段的值。
所以你需要用锁来保护整个操作。
由于方法体基本上是整个操作,因此您可以使用装饰器来执行此操作。例如,请参阅此答案:https://***.com/a/490090/34088
【讨论】:
出于好奇,“实例字段......是线程安全的”是什么意思? @EliBendersky:我猜他的意思是像self.Counter = Value
这样的操作是线程安全的。查看我刚刚找到的这篇文章:effbot.org/pyfaq/…
赋值不是线程安全的。假设您有 self.Counter=1,然后是 self.Counter=2,然后是 x=self.Counter。那么 x 可以根据线程切换得到 1 或 2,这使得赋值不是线程安全的。
我说的是 single 赋值是原子的(64 位类型除外)。见***.com/questions/4756536/…
如何增加一个问题,因为类的实例变量只存在于一个线程中。实例变量是否跨线程共享?【参考方案3】:
不,它不是线程安全的——两个线程本质上是同时修改同一个变量。是的,解决方案是threading
模块中的锁定机制之一。
顺便说一句,self.Counter
是一个实例变量,而不是一个类变量。
【讨论】:
我认为只有类变量不是线程安全的。实例变量应该是线程安全的,除非类实例是全局创建并传递给线程的。你怎么看? @variable 因为实例可以传递给不同的线程,所以你的观点没有意义。无论实现如何,事物都是线程安全的或不是线程安全的。【参考方案4】:self.Counter
是一个实例变量,所以每个线程都有一个副本。
如果您在__init__()
之外声明变量,它将是一个类变量。
该类的所有实例都将共享该实例。
【讨论】:
多个线程可以访问同一个对象的实例吗? @HarryJohnston 对此要非常小心。它可以增加相当多的复杂性。您可能需要进行某种形式的锁定。 @AustinHenley:是的,这就是重点。 OP 想知道实例变量是否是线程安全的。 我认为只有类变量不是线程安全的。实例变量应该是线程安全的,除非类实例是全局创建并传递给线程的。你怎么看?【参考方案5】:Atomos 库为 Python 原语和对象(包括原子计数器)提供原子(线程安全)包装器。它使用单写/多读锁。
【讨论】:
以上是关于Python 实例变量是线程安全的吗?的主要内容,如果未能解决你的问题,请参考以下文章
单例实例声明为 GetInstance 方法的静态变量,它是线程安全的吗? [复制]
单例实例声明为 GetInstance 方法的静态变量,它是线程安全的吗? [复制]
JSONDecoder和JSONEncoder类是线程安全的吗?