Python 实例变量是线程安全的吗?

问题描述 投票:0回答:5

好的,先检查以下代码:

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()
锁定这个资源,还有更好的主意吗?

python multithreading thread-safety
5个回答
50
投票

多个线程同时访问变量将导致竞争条件和不良行为。您必须依赖锁、RLock、信号量、条件、事件或队列。

以下文章有助于理解它们之间的区别以及何时在多线程环境中使用它们:


32
投票

使用实例字段

self.Counter
线程安全或“原子”或此处)。读取它或分配一个single值 - 即使它需要内存中的 4 个字节,你也永远不会得到一半变化的值。但操作
self.Counter = self.Counter + 1
并不是因为它读取值然后写入它 - 另一个线程可以在读取该字段之后和写回该字段之前更改该字段的值。

所以你需要用锁来保护整个操作。

由于方法体基本上是整个操作,因此您可以使用装饰器来完成此操作。请参阅此答案的示例:https://stackoverflow.com/a/490090/34088


15
投票

不,它不是线程安全的——两个线程本质上是同时修改同一个变量。是的,解决方案是

threading
模块中的锁定机制之一。

顺便说一句,

self.Counter
是一个实例变量,而不是类变量


2
投票

self.Counter
是一个实例变量,所以每个线程都有一个副本。

如果您在

__init__()
之外声明变量,它将是一个类变量。 该类的所有实例将共享该实例。


© www.soinside.com 2019 - 2024. All rights reserved.