序列解包是原子性的吗?

9

序列解包是原子操作吗?例如:

(a, b) = (c, d)

我认为它不是原子性的。编辑:我指的是在多线程的情况下的原子性,即整个语句是否像原子一样不可分割。请注意,上述内容中保留了HTML标记。

1
我有一种印象,你可能误解了软件开发中“原子性”的通常理解。你是在谈论线程安全吗?还是想知道a = c是否会在b = d之前执行? - Martijn Pieters
如果OP对后者有疑问,那么像(a, b) = (b, a)这样的交换值的操作会让OP感到非常困惑。这一定是关于线程安全的问题。 - Ted Hopp
@TedHopp:这个问题太模糊了,无法称之为问题。根据我的经验,元组解包本身就会引起很多混乱。 - Martijn Pieters
“原子性”在计算机语言中的意思是“一步完成,不可分割”。如果某个操作是原子的,那么这意味着该操作不能被另一个线程中断。 - Martijn Pieters
2个回答

11

这是一个操作,右侧的表达式在左侧赋值之前被评估:

>>> a, b = 10, 20
>>> a, b
(10, 20)
>>> b, a = a, b
>>> a, b
(20, 10)
>>> a, b = a*b, a/b
>>> a, b
(200, 2)

如果你在谈论多线程环境,那么赋值就不是原子性的;解释器使用单个操作码来评估元组赋值,但使用单独的操作码将结果存储到每个受影响的变量中:

>>> def t(self): a,b=20,20
... 
>>> dis.dis(t)
  1           0 LOAD_CONST               2 ((20, 20))
              3 UNPACK_SEQUENCE          2
              6 STORE_FAST               1 (a)
              9 STORE_FAST               2 (b)
             12 LOAD_CONST               0 (None)
             15 RETURN_VALUE        

然而,普通的赋值至少需要两个操作码(一个用于右侧表达式,一个用于存储结果),因此在Python中,总的来说,赋值不是原子性的。序列解包也不例外。

1
我认为这不是“原子”表达式。即使在高级语言中,“j = i +1”也不是原子表达式。我在Python中有“Semaphore”……我误解了问题吗? - Grijesh Chauhan
这并没有真正证明它是原子性的。问题在于在并发环境中是否以原子方式(即作为不可分割的操作)分配ab的值。我不认为它们是;也就是说,如果(a, b)应该在最后一步结束时具有值(200, 2),则另一个线程可能会看到(200, 10)(20, 2) - Ted Hopp
我误解了问题(或者更确切地说,我选择将非常简洁的问题解释为关于元组赋值的更常见问题)。我已经更新了它,包括线程安全信息。 - Martijn Pieters
@GrijeshChauhan:我强烈怀疑OP不理解原子性的常见含义。我已经要求澄清。 - Martijn Pieters

9

在多线程环境下绝对不是原子的,使用以下脚本进行测试:

import threading

a, b = 10, 10
finished = False
def thr():
    global finished
    while True:
        # if sequence unpacking and assignment is atomic then (a, b) will always
        # be either (10, 10) or (20, 20).  Could also just check for a != b
        if (a, b) in [(10, 20), (20, 10)]:
            print('Not atomic')
            finished = True
            break

t = threading.Thread(target=thr)
t.start()

while True:
    for i in range(1000000):
        a, b = 20, 20
        a, b = 10, 10
    if finished:
        t.join()
        break

本脚本在 CPython 2.6、2.7 和 3.2 上测试通过。在每个版本上,该脚本都打印“Not atomic”,并在不到一秒钟内退出。


1
你让我重新检查了操作码;虽然 unpack 只有一个,但是有两个 STORE_FAST 操作码,分别作用于两个变量。这给另一个线程在其中任意一个变量被赋值之间赋予不同的值提供了充足的机会。 - Martijn Pieters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接