【问题标题】:python dictionary is thread safe?python字典是线程安全的吗?
【发布时间】:2010-07-29 01:07:06
【问题描述】:

有人说python字典是线程安全的。这是否意味着在迭代字典时我可以或不能修改字典中的项目?

【问题讨论】:

    标签: python thread-safety


    【解决方案1】:

    其他答案已经正确解决了您的实际问题:

    这是否意味着我可以或不能修改 字典中的项目同时 迭代它?

    通过解释线程安全与问题无关,并且无论如何,不​​,您不能在迭代时修改字典。

    但是,你的问题的标题关于线程安全的,你从以下开始:

    有人说python字典是 线程安全

    我不知道“某些人”是谁,但是,如果他们确实这么说(而不是你误解了他们所说的内容;-)没有严格的限定,那他们就错了。

    一些操作,那些不改变字典中的键集的操作,在当前的 CPython 实现中恰好是线程安全的——但你应该依靠这一点,除非您严格控制运行代码的 Python 版本,因为 Python 的语言规范不能保证这种线程安全,因此其他实现(包括 CPython 的未来版本)可能不会提供它。

    如果每个线程都只是“读取”字典(对其进行索引、循环等),并且没有线程对其执行任何赋值或删除,那么这种情况在当前的 CPython 实现中是安全的;事实上,如果某个线程为已经存在的键分配一个新值,那也是线程安全的(其他线程可能会看到该键的前一个值或下一个值,具体取决于线程的计时方式,但在当前的 CPython 实现中不会出现崩溃、死锁和突然出现的疯狂值)。

    然而,像 d[k] += 1 这样的操作(假设 k 以前存在,并且它的值是一个数字)不是正确地说是线程安全的(比 += 的其他情况更重要!)因为它可以被视为d[k] = d[k] + 1——它可能发生在竞争条件下的两个线程都读取d[k]的旧值,然后将其加一,并存储相同的新值在插槽中...所以整体效果是仅将其增加一,而不是像通常发生的那样增加二。

    回到你的另一个问题......“只读取”字典,为字典中已经存在的键分配新值,也是你可以在 a 的正文中做的事情在 dict 上迭代的循环 - 您不能更改 dict 中的键集(不能添加任何键,也不能删除任何键),但允许为现有键设置新值的特定操作.在这种情况下,允许的操作确实包括在线程情况下会出现问题的+=。例如:

    >>> d = dict.fromkeys(range(5), 0)
    >>> for k in d: d[k] += 1
    ... 
    >>> d
    {0: 1, 1: 1, 2: 1, 3: 1, 4: 1}
    

    并且这个行为是由 Python 的标准化语义保证的,因此该语言的不同实现都应该保留它。

    【讨论】:

    • 你首先说“如果每个线程都只是“读取”字典(索引它,循环它等),并且 没有线程对其执行任何赋值或删除,那么这种情况在当前的 CPython 实现中是安全的”,最后您说“为现有键设置新值的特定操作是允许的。......这种行为由 Python 的标准化语义保证,因此该语言的不同实现都应该保留它”。我觉得很难区分这两种情况。你能详细说明一下吗?提前致谢!
    【解决方案2】:

    这两个概念完全不同。 Thread safety 表示两个线程不能同时修改同一个对象,从而使系统处于不一致的状态。

    也就是说,您不能在迭代字典时对其进行修改。请参阅documentation.

    字典 p 不应该在迭代过程中发生变异。它是安全的(从 Python 2.1 开始) 在遍历字典时修改键的值,但只要 一组键不会改变。

    【讨论】:

    • 我认为它们是相关的。如果一个线程迭代而另一个线程修改了字典怎么办?
    【解决方案3】:

    没有。如果您尝试迭代在两次迭代之间改变了大小的字典,最新版本的 python 将引发异常。

    >>> d={'one':1, 'two':2}
    >>> for x in d:
    ...    d['three']=3
    ...    print x
    ...
    two
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    RuntimeError: dictionary changed size during iteration
    

    请注意,您不需要使用线程来查看此内容

    【讨论】:

      【解决方案4】:

      如果您同时添加删除另一个线程中的元素,您将无法遍历字典。你会得到“RuntimeError: dictionary changed size during iteration”或“KeyError”错误。

      live example,你就可以玩了。

      是的,您可以在不同线程中同时进行迭代、更改元素的值、获取其中的元素而不会出现异常。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-10-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-05
        • 1970-01-01
        • 2023-04-02
        相关资源
        最近更新 更多