为什么';追加';Python方法&x27;列表';当i=i+;1不是原子的吗?

为什么';追加';Python方法&x27;列表';当i=i+;1不是原子的吗?,python,thread-safety,atomic,gil,Python,Thread Safety,Atomic,Gil,根据,Python上的append操作是原子的。同时,加法操作不是原子的: i = i + 1 我知道Python GIL正在强制执行append操作,使其成为原子操作。我的问题是,为什么GIL不在加法运算中执行相同的操作 考虑以下代码: In [24]: L = [] In [25]: def func1(): ...: L.append(2) ...: In [26]: i = 0 In [27]: def func2(): ...: i =

根据,Python上的
append
操作是原子的。同时,加法操作不是原子的:

i = i + 1
我知道Python GIL正在强制执行
append
操作,使其成为原子操作。我的问题是,为什么GIL不在加法运算中执行相同的操作

考虑以下代码:

In [24]: L = []

In [25]: def func1():
    ...:     L.append(2)
    ...:

In [26]: i = 0

In [27]: def func2():
    ...:     i = i + 2
    ...:
两个函数的字节码如下所示:

In [28]: dis.dis(func1)
  2           0 LOAD_GLOBAL              0 (L)
              2 LOAD_METHOD              1 (append)
              4 LOAD_CONST               1 (2)
              6 CALL_METHOD              1
              8 POP_TOP
             10 LOAD_CONST               0 (None)
             12 RETURN_VALUE

In [29]: dis.dis(func2)
  2           0 LOAD_FAST                0 (i)
              2 LOAD_CONST               1 (2)
              4 BINARY_ADD
              6 STORE_FAST               0 (i)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE

Python确保线程切换只在字节码指令之间发生。使
append
操作原子化的字节码指令有什么区别?

因为
append
操作会对所附加的列表产生副作用,如果它不是原子的,它就不可能有任何语义(并且很有可能转储核心)。相比之下,加法是一个简单得多的过程,也是可交换的,因此在一个线程中执行i+=1,在另一个线程中执行i-=1,当烟雾消失时,将得到相同的最终结果。

加法本身是原子的(它只是一个二进制加法)。但i=i+2基本上是两条指令:计算右侧(i+1),然后将变量左侧(i)设置为等于该结果。同意。i=i+2跨越多个字节码指令,因此它不是原子指令。但对我来说,追加操作似乎也跨越了多个字节码指令。那么追加操作是如何原子的?@SimonR,Re,“加法本身是原子的。”通常,当我们说“X是原子的”时,我们谈论的是其他线程如何看待该操作,而由于线程通过共享内存进行通信,我们谈论的是该操作如何影响共享内存位置。加法本质上不是原子的,因为为了对共享变量执行“加法”,程序必须从内存中获取一个或两个操作数,然后将结果写入内存。Re,“在一个线程中执行i+=1,在另一个线程中执行i-=1,当烟雾消散时,将给出相同的最终结果。”我不知道在Python中是否是这样,“但在大多数多线程环境中并非如此。”Solomon慢-有趣的评论。+1/-1如何给出不同的结果?@mherzog CPU必须至少执行三次操作才能递增共享变量;从内存中获取值,计算新值,然后将新值存储回内存。如果两个线程在第一步中获取相同的值,那么它们都将写回相同的新值,并且变量实际上只递增一次。同步原语(例如互斥)是防止这种情况发生的必要条件。在CPython中,是一个互斥锁,它为每个Python语句锁定和解锁。其他语言没有GIL。