我需要同步不可变对象吗?

发布于 2025-01-15 20:33:28 字数 398 浏览 5 评论 0 原文

我有一个不可变的对象(一个float),它由一个线程“写入”并由另一个线程读取。我需要同步吗?

class Foo:
   def __init(self):
      self._bar = None

   def writeAttribute(self, newValue):
      self._bar = newValue

   def readAttribute(self):
      return self._bar

请注意,对于同一实例,writeAttributereadAttribute 是从不同线程调用的。我的理解是 readAttribute 返回一个对旧值或新值的“引用”,但不返回任何中间值。

I have an immutable object (a float) that is "written" by one thread and read by another one. Do I need synchronization for that?

class Foo:
   def __init(self):
      self._bar = None

   def writeAttribute(self, newValue):
      self._bar = newValue

   def readAttribute(self):
      return self._bar

Note that writeAttribute and readAttribute are called from different threads for the same instance. My understanding is that readAttribute returns a "reference" either to the old value or the new one, but nothing in between.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ˉ厌 2025-01-22 20:33:28

在 CPython(来自 python.org)中,全局解释器锁(“GIL”)确保一次只有一个线程正在执行 Python 字节码。此外,线程可以在字节码之间被抢占,但不能在字节码“内部”被抢占。这意味着字节码是原子的。

这意味着采用单个字节码的每个操作都是线程安全的,作为 GIL 的副作用

考虑以下代码,dis 模块用于查看函数的字节码:


In [2]: bar = 3.14
Out[2]: 3.14

In [3]: def modify(new):
   ...:     global bar
   ...:     bar = new
   ...:     

In [4]: dis.dis(modify)
  3           0 LOAD_FAST                0 (new)
              2 STORE_GLOBAL             0 (bar)
              4 LOAD_CONST               0 (None)
              6 RETURN_VALUE

为全局 bar 分配新值仅需要一个 STORE_GLOBAL 字节码。所以这应该是线程安全的,因为 bar 总是指向一个对象。它的价值永远是不确定的。

值得注意的是; float 对象是不可变的并不重要。对该对象(示例中的 bar)的引用是可变的。您希望保持定义和明确的就是该引用。

其次,我建议您使用 @property 装饰器。它被认为更Pythonic。

编辑

另请参阅常见问题解答:什么类型的全局值突变是线程安全的?

Edit2

当然:当有疑问时,使用例如 Lock.

In CPython (the one from python.org) the Global Interpreter Lock ("GIL") ensures that only one thread at a time is executing Python bytecodes. Also, threads can be preempted in between bytecodes, but not "inside" them. That means bytecodes are atomic.

That means that every action that takes a single bytecode is thread-safe as a side-effect of the GIL.

Consider the following code, were the dis module is used to see bytecodes for a function:


In [2]: bar = 3.14
Out[2]: 3.14

In [3]: def modify(new):
   ...:     global bar
   ...:     bar = new
   ...:     

In [4]: dis.dis(modify)
  3           0 LOAD_FAST                0 (new)
              2 STORE_GLOBAL             0 (bar)
              4 LOAD_CONST               0 (None)
              6 RETURN_VALUE

Assigning a new value to the global bar takes only a single STORE_GLOBAL bytecode. So that should be thread-safe, in the sense that bar always points to an object. Its value is never undetermined.

Of note; it doesn't matter that a float object is immutable. The reference to that object (bar in the example) is mutable. It is that reference that you want to keep defined and unambiguous.

Second, instead of a getter/setter method in your class Foo I would propose that you use @property decorator. It is considered more Pythonic.

Edit

See also the FAQ: What kinds of global value mutation are thread-safe?

Edit2

Of course: when in doubt, use e.g. a Lock.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文