参考以下链接:http ://docs.python.org/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe
我想知道以下是否:
(x, y) = (y, x)
将保证在 cPython 中是原子的。(x 和 y 都是 python 变量)
参考以下链接:http ://docs.python.org/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe
我想知道以下是否:
(x, y) = (y, x)
将保证在 cPython 中是原子的。(x 和 y 都是 python 变量)
让我们来看看:
>>> x = 1
>>> y = 2
>>> def swap_xy():
... global x, y
... (x, y) = (y, x)
...
>>> dis.dis(swap_xy)
3 0 LOAD_GLOBAL 0 (y)
3 LOAD_GLOBAL 1 (x)
6 ROT_TWO
7 STORE_GLOBAL 1 (x)
10 STORE_GLOBAL 0 (y)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
看起来它们不是原子的:x 和 y 的值可以由另一个线程在LOAD_GLOBAL
字节码之间、在 之前或之后ROT_TWO
以及在STORE_GLOBAL
字节码之间进行更改。
如果你想原子地交换两个变量,你需要一个锁或一个互斥锁。
对于那些渴望经验证明的人:
>>> def swap_xy_repeatedly():
... while 1:
... swap_xy()
... if x == y:
... # If all swaps are atomic, there will never be a time when x == y.
... # (of course, this depends on "if x == y" being atomic, which it isn't;
... # but if "if x == y" isn't atomic, what hope have we for the more complex
... # "x, y = y, x"?)
... print 'non-atomic swap detected'
... break
...
>>> t1 = threading.Thread(target=swap_xy_repeatedly)
>>> t2 = threading.Thread(target=swap_xy_repeatedly)
>>> t1.start()
>>> t2.start()
>>> non-atomic swap detected
是的,是的,它会的。
Kragen Sitaker 写道:
有人推荐使用成语
spam, eggs = eggs, spam
获得线程安全的交换。这真的有效吗?(...)
因此,如果此线程在第一个 LOAD_FAST
和最后一个 STORE_FAST 之间的任何地方失去控制,则另一个线程可能会将一个值存储
到“b”中,然后该值将丢失。没有什么可以阻止这种
情况发生,不是吗?没有。通常,即使是简单的赋值也不一定是线程安全的,因为执行赋值可能会调用对象上的特殊方法,这些方法本身可能需要许多操作。 希望该对象将在内部锁定其“状态”值,但情况并非总是如此。
但这实际上取决于“线程安全”在特定应用程序中的含义,因为在我看来,这种安全性有很多级别的粒度,因此很难谈论“线程安全”。Python 解释器将免费为您提供的唯一一件事是,即使使用本机线程,内置数据类型也应该免受内部损坏。换句话说,如果两个线程具有
a=0xff
anda=0xff00
, a 将以一个或另一个结束,但0xffff
如果 a 不受保护,则不会像在其他一些语言中可能发生的那样意外。话虽如此,Python 也倾向于以这样一种方式执行,如果您愿意稍微生活在边缘并且隐含依赖于实际使用的对象,那么您可以在没有正式锁定的情况下侥幸逃脱。不久前,在 clp 中进行了类似的讨论——在 groups.google.com 中搜索“关键部分和互斥锁”线程等。
就个人而言,我在任何多线程应用程序中显式锁定共享状态(或使用为在线程之间正确交换共享信息而设计的结构,例如)。
Queue.Queue
在我看来,这是防止维护和进化的最佳保护。-- --大卫