Python dict.update()是否线程安全?
我想知道dict.update()是否是python线程安全的。我读过相关的问题,但没有一个能完全回答我的问题 我的问题非常具体和简单。例如,我已经有一本本地字典Python dict.update()是否线程安全?,python,multithreading,dictionary,Python,Multithreading,Dictionary,我想知道dict.update()是否是python线程安全的。我读过相关的问题,但没有一个能完全回答我的问题 我的问题非常具体和简单。例如,我已经有一本本地字典d2。我只需要用d2更新全局字典d,如下所示d开始时为空,然后填充不同的线程。每个线程中的d2可能有与d重叠的条目(不要认为这很重要)。它是线程安全的吗 import dis def f(d): d2 = {1:2, 3:4} d.update(d2) print(dis.dis(f)) 字节码如下所示: 10 0
d2
。我只需要用d2
更新全局字典d
,如下所示<代码>d开始时为空,然后填充不同的线程。每个线程中的d2
可能有与d
重叠的条目(不要认为这很重要)。它是线程安全的吗
import dis
def f(d):
d2 = {1:2, 3:4}
d.update(d2)
print(dis.dis(f))
字节码如下所示:
10 0负载常数1(2)
2负载常数2(4)
4荷载常数3((1,3))
6建筑施工图2
8门店快速1(d2)
11 10快速加载0(d)
12加载属性0(更新)
14负载快速1(d2)
16调用函数1
18件流行上衣
20负载常数0(无)
22返回值
看起来,
16 CALL\u FUNCTION
是更新字典的原子函数。所以它应该是线程安全的?如果键是内置哈希类型的组合,通常是“是”,那么.update()
是线程安全的。特别是,对于使用整数键的示例,是的
import dis
def f(d):
d2 = {1:2, 3:4}
d.update(d2)
print(dis.dis(f))
但一般来说,不会。在dict中查找键可以调用用户提供的\uuuuuu hash\uuuuuu()
和\uuuuuuu eq\uuuuuu()
方法中的任意用户定义的Python代码,这些方法可以做任何事情,包括在所涉及的dict上执行自己的突变。一旦实现调用Python代码,其他线程也可以运行,包括可能也在变异d1
和/或d2
的线程
这对于内置哈希类型(int、string、float、tuple等)来说不是一个潜在的问题——它们计算哈希代码和确定相等性的实现是纯功能性的(确定性的,没有副作用),并且不会释放GIL(全局解释器锁)
这就是关于CPython(Python的C实现)的全部内容。在其他实现下,答案可能不同!《语言参考手册》对此没有提及。如果您可以使用外部库,您可以查看一下
从他们的自述:
Dict允许上下文管理的线程安全和可变迭代
通过一把锁
例如,从他们的:
pip安装锁定dict
import locked_dict
expected = 0
d = locked_dict.LockedDict()
assert len(d) == expected
assert bool(d) is False
assert d is not True
assert hasattr(d, '_lock')
empty_d = {}
assert d == empty_d
plain_old_d = {999: 'plain old dict', 12345: 54321}
assert d != plain_old_d
with d as m:
assert len(m) == expected
assert bool(m) is False
assert m is not True
assert hasattr(m, '_lock')
assert m != plain_old_d
assert m == empty_d
m[0] = ['foo']
expected += 1
assert len(m) == expected
assert bool(m) is True
assert m is not False
assert m != plain_old_d
assert m != empty_d
m.clear()
expected -= 1
assert len(m) == expected
assert bool(m) is False
assert m is not True
assert m != plain_old_d
assert m == empty_d
不要以为这个库已经有3年历史了,尽管它可能仍然与您的用例相关我认为它不是。请参阅相关的
CALL_函数
基本上只是一个能记住返回位置的goto。它与被调用函数的原子性无关。@chepner那么调用的原子性是什么呢?人们说每个字节码指令都是原子的。我从来都不太了解所有这些线程切换都发生在字节码边界上(并且只发生在字节码边界上),因此任何使用一个以上字节码的操作如果没有显式锁定就不能是原子的。单个字节码可能以原子方式执行,也可能不以原子方式执行,这取决于它们的实现方式。在您的示例中,除了(可能!)LOAD\u ATTR
和CALL\u函数之外,所有函数都是原子函数。这里没有通用的原理,只有详细的实现知识。更多信息:CALL\u函数
刚刚开始调用;你仍然需要考虑函数本身的字节码(或者它的一般实现)。谢谢你的详细答案!是的,我使用的是CPython,我的键是int/str。