dict.update()线程安全吗?

时间:2019-02-13 23:20:56

标签: python multithreading dictionary

我想知道dict.update()是否是python线程安全的。我已经阅读了相关的问题,但是没有一个能完全解决我的问题。

我的问题非常具体和简单。例如,我已经有本地字典d2。我只需要使用d更新全局字典d2,如下所示。 d开始为空,并充满了不同的线程。每个线程中的d2可能与d有重叠的条目(不要紧)。线程安全吗?

import dis

def f(d):
    d2 = {1:2, 3:4}
    d.update(d2)

print(dis.dis(f))

字节码如下:

 10           0 LOAD_CONST               1 (2)
              2 LOAD_CONST               2 (4)
              4 LOAD_CONST               3 ((1, 3))
              6 BUILD_CONST_KEY_MAP      2
              8 STORE_FAST               1 (d2)

 11          10 LOAD_FAST                0 (d)
             12 LOAD_ATTR                0 (update)
             14 LOAD_FAST                1 (d2)
             16 CALL_FUNCTION            1
             18 POP_TOP
             20 LOAD_CONST               0 (None)
             22 RETURN_VALUE

看起来16 CALL_FUNCTION是更新字典的原子函数。所以应该是线程安全的吗?

2 个答案:

答案 0 :(得分:8)

如果键是内置的可哈希类型的组合,通常为“是”,则.update()是线程安全的。特别是,对于您使用整数键的示例,是的。

但是一般,不。在字典中查找键 可以在用户提供的__hash__()__eq__()方法中调用任意用户定义的Python代码,这些方法可以执行任何操作-包括执行其自己在涉及的字典上的变异。该实现调用Python代码后,其他线程也可以运行,包括 也可能正在突变d1和/或d2的线程。

对于内置的可哈希类型(int,字符串,浮点数,元组等)来说,这不是潜在的问题-它们的实现可用于计算哈希码并确定相等性是纯粹的功能(确定性且无副作用),并且不会释放GIL(全局解释器锁)。

关于CPython(Python的C实现)。在其他实现下,答案可能会有所不同! 《语言参考手册》对此保持沉默。

答案 1 :(得分:0)

如果可以使用外部库,则可以研究locked-dict

摘自自述文件:

  

Dict允许上下文管理的线程安全且可变的迭代   通过锁。

例如,来自他们的tests

pip install locked-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年。