【问题标题】:Are Python instance variables thread-safe?Python 实例变量是线程安全的吗?
【发布时间】: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()锁定这个资源,有更好的办法吗?

【问题讨论】:

    标签: python multithreading thread-safety


    【解决方案1】:

    您可以使用锁、RLock、信号量、条件、事件和队列。
    这篇文章对我的帮助很多
    看看吧:Laurent Luce's Blog

    【讨论】:

      【解决方案2】:

      使用实例字段self.Counterthread safe or "atomic"。读取它或分配一个 single 值 - 即使它在内存中需要 4 个字节,您也永远不会得到一半更改的值。但是操作self.Counter = self.Counter + 1并不是因为它读取值然后写入它——另一个线程可以在读取之后和写回之前更改字段的值。

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

      由于方法体基本上是整个操作,因此您可以使用装饰器来执行此操作。例如,请参阅此答案:https://stackoverflow.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 位类型除外)。见stackoverflow.com/questions/4756536/…
      • 如何增加一个问题,因为类的实例变量只存在于一个线程中。实例变量是否跨线程共享?
      【解决方案3】:

      不,它不是线程安全的——两个线程本质上是同时修改同一个变量。是的,解决方案是threading 模块中的锁定机制之一。

      顺便说一句,self.Counter 是一个实例变量,而不是一个类变量

      【讨论】:

      • 我认为只有类变量不是线程安全的。实例变量应该是线程安全的,除非类实例是全局创建并传递给线程的。你怎么看?
      • @variable 因为实例可以传递给不同的线程,所以你的观点没有意义。无论实现如何,事物都是线程安全的或不是线程安全的。
      【解决方案4】:

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

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

      【讨论】:

      • 多个线程可以访问同一个对象的实例吗?
      • @HarryJohnston 对此要非常小心。它可以增加相当多的复杂性。您可能需要进行某种形式的锁定。
      • @AustinHenley:是的,这就是重点。 OP 想知道实例变量是否是线程安全的。
      • 我认为只有类变量不是线程安全的。实例变量应该是线程安全的,除非类实例是全局创建并传递给线程的。你怎么看?
      【解决方案5】:

      Atomos 库为 Python 原语和对象(包括原子计数器)提供原子(线程安全)包装器。它使用单写/多读锁。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-08-30
        • 1970-01-01
        • 1970-01-01
        • 2013-04-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-20
        • 2021-05-12
        相关资源
        最近更新 更多