Python 检查字典中的密钥是原子操作吗?

Python 检查字典中的密钥是原子操作吗?,python,Python,检查一个键是否在字典中-如果键在mydict中-是一个原子操作吗 如果不是,那么如果一个线程正在检查密钥,而另一个线程正在修改字典,会有任何负面影响吗?检查线程不会修改字典,它只是根据键的存在而表现出不同的行为。我不认为“原子”是您真正感兴趣的 如果您没有使用CPython(或者您正在使用,但是其中一个线程正在运行不在GIL下的C代码…),那么它肯定不是原子的,但在某些条件下它可能是安全的 如果您使用的是CPython,那么它是原子的,因为中的是一个单字节码op(COMPARE_op 6),并且

检查一个键是否在字典中-
如果键在mydict中-
是一个原子操作吗

如果不是,那么如果一个线程正在检查密钥,而另一个线程正在修改字典,会有任何负面影响吗?检查线程不会修改字典,它只是根据键的存在而表现出不同的行为。

我不认为“原子”是您真正感兴趣的

如果您没有使用CPython(或者您正在使用,但是其中一个线程正在运行不在GIL下的C代码…),那么它肯定不是原子的,但在某些条件下它可能是安全的

如果您使用的是CPython,那么它是原子的,因为
中的
是一个单字节码op(
COMPARE_op 6
),并且可能更有用的是,实际的哈希表查找本身肯定发生在GIL下,任何潜在的相等比较肯定发生在保证活动的对象上。但它仍然可能是不安全的,除非在某些条件下

首先,你在这里做的更高级别的操作本质上是活泼的。如果线程1可以同时执行
d['foo']=3
deld['foo']
,则线程0在d
中调用
'foo',则没有正确答案。这不是原子与否的问题,这里没有排序

但是,如果您在应用程序级别有某种明确的排序,因此有一个正确的答案,那么只有当两个线程都持有GIL时,您才能保证得到正确的答案。我想这就是你要问的,是吗

这甚至只在CPython中才可能,即使在那里,它也相当于保证当您尝试对它进行
散列
=
时,您放入
dict
中的任何对象都不会释放GIL,这通常是很难保证的

现在,如果另一个线程只是替换与键关联的值,而不是更改键集,该怎么办?然后有一个正确的答案,只要
dict
实现避免为该操作修改哈希表,它就可以同时使用。至少在CPython的版本中,直到2010年7月29日发布的任何版本。亚历克斯·马泰利在他的回答中间接地保证了这一点。因此,在这种受限的情况下,您在CPython和其他实现中都是安全的,但是在依赖它之前,您应该先阅读代码


正如在评论中指出的,您可能最终比较查找值的键并不保证是不变的,因此即使另一个线程不做任何更改键集的事情,也不能绝对保证您会得到正确的答案。(您可能需要创建一个病理性密钥类型以使其失败,但它仍然是一个合法的密钥类型。)

可能的重复:您可以自己检查这一点,所需的时间远远少于任何人回答的时间。我非常确定,由于CPython中的全局解释器锁,大多数内置字典方法都是原子的,但这只是一个实现细节,而不是语言保证。@Tim事实上,确实如此(请参见下面Alex Martelli的答案),尽管这只是一小部分。尽管如此,这还是值得一个独立的问题。@John:你到底会如何“自己检查这个”?这里讨论的种族条件很难重现。你说“一个保证是……不可变的对象。”:实际上,它不保证是不可变的。大多数情况下,许多可散列的python类型也是不可变的;但是没有合理的保证;例如,我可以用一个非常不体谅人的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。python既不能防止也不能检测这种情况。唯一的保证是,即使这样,行为不端的对象仍然可以在dict中访问,使用
keys()
@TokenMacGuy:这实际上比非法但无法预防更糟糕;键通过eq改变值实际上是合法的,只要它不违反值相等意味着散列相等的规则。(所以不可预测地从假变真是违法的,但不是相反。)嗯。。。我怎么能利用这个漏洞作恶?(开始咯咯地笑)谢谢@abarnert,这就是我要找的。@macguy:我不确定合法但病理病例比非法但无法检测的要好。违反法律本身就让它更邪恶,对吗?还是更多的金属?我不知道这是什么感觉…