python - 为什么 reversed() 消除了线程安全?

标签 python thread-safety cpython python-internals gil

CPython 中确保迭代线程安全的一个常见习惯用法是使用 tuple() .
例如 - tuple(dict.items())即使项目被不同的线程删除,也保证在 CPython 中是线程安全的。
这是因为解释器在运行这些 C 函数时不运行 eval 循环并且不释放 GIL。我已经测试过了,效果很好。
然而,tuple(reversed(dict.items()))似乎不是线程安全的,我不明白为什么。它没有运行任何 Python 函数,也没有明确发布 GIL。如果我从 dict 中删除 key ,为什么我仍然收到错误消息虽然它在不同的线程上运行?

最佳答案

修改 dict 的大小在迭代它时总是会出错。原因tuple(d.items())是线程安全的,因为迭代器的检索和迭代都发生在同一个 C 函数中。d.items()创建一个 dict_items对象,但还没有迭代器。这就是为什么仍然反射(reflect)字典大小变化的原因:

>>> d = {'a': 1, 'b': 2}
>>> view = d.items()
>>> del d['a']
>>> list(view)
[('b', 2)]
但是,一旦检索到迭代器,字典大小 must not change不再:
>>> d = {'a': 1, 'b': 2}
>>> iterator = iter(d.items())
>>> del d['a']
>>> list(iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
这就是使用 reversed 时发生的情况: 它 creates a reverse iterator dict 项目。导致麻烦的是迭代器部分,因为一旦创建了迭代器,底层 dict 就不能改变大小:
>>> d = {'a': 1, 'b': 2}
>>> r = reversed(d.items())
>>> r  # Note the iterator here.
<dict_reverseitemiterator object at 0x7fb3a4aa24f0>
>>> del d['a']
>>> list(r)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
所以在具体的例子中tuple(reversed(dict.items()))迭代器 reversed(dict.items())创建原始 dict 的内容,然后由 tuple 迭代。 .然而,这个迭代器要求 dict 的大小不变。就像 tuple(iter(dict.items()))只是按相反的顺序。
关于 GIL 开关,eval 循环在获取 reversed() 的结果时运行。 ,创建迭代器后,将其发送到 tuple()用于迭代。请参阅以下拆卸:
>>> dis.dis("tuple({}.items())")
  1           0 LOAD_NAME                0 (tuple)
              2 BUILD_MAP                0
              4 LOAD_METHOD              1 (items)
              6 CALL_METHOD              0
              8 CALL_FUNCTION            1
             10 RETURN_VALUE
>>> dis.dis("tuple(reversed({}.items()))")
  1           0 LOAD_NAME                0 (tuple)
              2 LOAD_NAME                1 (reversed)
              4 BUILD_MAP                0
              6 LOAD_METHOD              2 (items)
              8 CALL_METHOD              0
             10 CALL_FUNCTION            1
             12 CALL_FUNCTION            1
             14 RETURN_VALUE

关于python - 为什么 reversed() 消除了线程安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66003452/

相关文章:

python - dataframe.hist() 具有不同的 bin 大小

PHP:pcntl_fork() 的真正作用是什么?

python - 用另一个字符替换字符串中所有出现的所有字符,而不会相互干扰

java - 这段代码线程安全吗?静态方法修改httpservletrequest

c++ - 为什么只有一个锁和一个原子计数器的条件变量会错误地唤醒?

python - Python的序列协议(protocol)是什么?

string - cpython的字符串实习的规则是什么?

Python字符串 'in'算子实现算法和时间复杂度

python - 通过序列化器创建多模型实例怎么样?

python - 如何在 Python3 中将字符串数字列表转换为 int 数字?