是python变量赋值原子?

jld*_*ont 26 python signals

假设我正在使用signal处理程序来处理间隔计时器.

def _aHandler(signum, _):
  global SomeGlobalVariable
  SomeGlobalVariable=True
Run Code Online (Sandbox Code Playgroud)

我可以设置SomeGlobalVariable而不必担心,在设置SomeGlobalVariable(即Python VM正在执行字节码来设置变量)的不太可能的情况下,信号处理程序中的赋值会破坏某些东西吗?(即亚稳态)

更新:我特别感兴趣的是在处理程序之外进行"复合赋值".

(也许我在想"低级别",这一切都在Python中得到了解......来自嵌入式系统的背景,我不时有这些冲动)

Ale*_*lli 21

简单的赋值简单变量是"原子"又名线程(复合赋值如+=或转让的项目或对象的属性不需要,但你的例子是一个简单的赋值给一个简单的,虽然全球性的,可变的,因此是安全的).

  • 这指定在哪里?-1缺乏权威性参考. (4认同)
  • 如果处理程序执行(例如)`gvar = 3`,`gvar`最初为7,而处理程序外的代码执行(例如)`gvar + = 2`,则`gvar`最终可能为3,5 ,或9,取决于操作最终如何交错.这在技术上是"安全的"(意思是,过程不会崩溃;-)但不太可能在语义上正常. (2认同)
  • @Elyse,**没有**没有“权威参考”(我想我可以引用我自己的书,“权威”只是因为成为畅销书,但是,这到底有什么帮助?-)——所以,在你的世界(天空是什么颜色?),如果这个问题没有得到解答,世界会变得更好吗?因为不存在“权威参考”?!我的老朋友和同事 effbot 有 http://effbot.org/pyfaq/what-kinds-of-global-value-mutation-are-thread-safe.htm ——但他现在将其标记为过时,并且无论如何,为什么 effbot 比 martellibot 更权威?-) 多么悲伤......! (2认同)
  • 官方文件将被视为权威,我会说.如果没有记录,那么它是依赖于实现的,不是吗? (2认同)
  • @ R.MartinhoFernandes,绝对依赖于实现-在现实世界中,当然,CPython参考实现是如此占主导地位,以至于所有其他大多数人都试图坚持其(已记录或未记录)的行为,从而使该问题成为理论上和学究上的问题之一关联。鉴于此,我认为,与主导或拒绝帮助相比,在主导实现的现实世界中用“实际发生的情况”回答问题仍然具有积极价值,即使不存在“权威性参考”(也不太可能出现)时间很快)。 (2认同)
  • 如果标准没有明确指出简单分配是原子的,那么答案应该提到行为取决于实现。目前,答案是强有力的主张,而没有引用任何权威资料,所以-1。 (2认同)

Cir*_*四事件 5

Google的风格指南不建议这样做

我并不是说Google样式指南是最终的真理,但“线程”部分基本原理提供了一些见解(突出之处是我的观点):

不要依赖内置类型的原子性。

尽管Python的内置数据类型(例如字典)似乎具有原子操作,但在某些极端情况下它们不是原子操作(例如,如果将__hash____eq__实现为Python方法),则不应依赖其原子性。您也不应该依赖于原子变量赋值(因为这又取决于字典)。

使用Queue模块的Queue数据类型作为线程之间通信数据的首选方式。否则,请使用线程模块及其锁定原语。了解条件变量的正确用法,以便可以使用threading.Condition而不是使用较低级别的锁。

所以我的解释是,在Python中,一切都像dict,并且当您a = b在后端进行操作时globals['a'] = b,这很不好,因为dict 不一定是线程安全的。

对于单个变量,Queue这不是理想的选择,因为我们希望它仅包含一个元素,并且我无法在stdlib中找到可以自动同步.set()方法的理想容器。所以现在我只做:

import threading

myvar = 0
myvar_lock = threading.Lock()
with myvar_lock:
    myvar = 1
with myvar_lock:
    myvar = 2
Run Code Online (Sandbox Code Playgroud)

有趣的是Martelli似乎并不介意 Google样式指南的建议:-)(他在Google工作)

我想知道CPython GIL是否对以下问题有影响:CPython 中的全局解释器锁(GIL)是什么?

该线程还表明CPython字典是线程安全的,包括以下词汇表引用,明确引用了该词汇表:https://docs.python.org/3/glossary.html#term-global-interpreter-lock

通过使对象模型(包括关键的内置类型,如dict)隐式地安全地防止并发访问,从而简化了CPython的实现。


Iza*_*ana 5

您可以尝试dis查看底层字节码。

import dis

def foo():
    a = 1


dis.dis(foo)

Run Code Online (Sandbox Code Playgroud)

产生字节码:

# a = 1
5             0 LOAD_CONST               1 (1)
              2 STORE_FAST               0 (a)
Run Code Online (Sandbox Code Playgroud)

所以赋值是一个单独的 python 字节码(指令 2),它在 CPython 中是原子的,因为它一次执行一个字节码。

而添加一个a += 1

def foo():
    a = 1
    a += 1
Run Code Online (Sandbox Code Playgroud)

产生字节码:

# a+=1
6             4 LOAD_FAST                0 (a)
              6 LOAD_CONST               1 (1)
              8 INPLACE_ADD
             10 STORE_FAST               0 (a)
Run Code Online (Sandbox Code Playgroud)

+= 对应4条指令,不是原子指令。