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 方法的静态变量,它是线程安全的吗? [复制]

Controller是单例模式的吗?如何保证线程安全?

JSONDecoder和JSONEncoder类是线程安全的吗?

ServerEndpointConfig.Configurator 实例应该是线程安全的吗?

java中的++i是线程安全的吗?