24

我根本没有在 Python 中使用过线程,并且作为一个完全陌生的人问这个问题。

我想知道是否defaultdict是线程安全的。让我解释一下:

我有

d = defaultdict(list)

默认情况下会为丢失的键创建一个列表。假设我有多个线程同时开始执行此操作:

d['key'].append('value')

最后,我应该以['value', 'value']. 但是,如果defaultdict不是线程安全的,如果线程1在检查之后和之前 让给线程2,就会导致交错,另一个线程可能会在其中创建列表并追加。if 'key' in dictd['key'] = default_factory()d['key']'value'

然后当线程 1再次执行时,它会继续执行,d['key'] = default_factory()这会破坏现有的列表和值,我们最终会进入['key'].

我查看了 defaultdict 的 CPython 源代码。但是,我找不到任何锁或互斥锁。我猜它不是线程安全的,只要它被记录在案。

昨晚在 IRC 上有些人说 Python 上有 GIL,所以它在概念上是线程安全的。有人说线程不应该在 Python 中完成。我很困惑。想法?

4

1 回答 1

31

在这种特定情况下,它是线程安全的。

要知道为什么理解 Python 何时切换线程很重要。CPython 只允许在 Python 字节码步骤之间切换线程。这就是 GIL 的用武之地;每 N 字节代码指令释放锁,并且可以发生线程切换。

d['key']代码由一个字节码 ( ) 处理,该字节码触发BINARY_SUBSCR.__getitem__()在字典上调用的方法。

A defaultdict,配置list为默认值工厂,并使用字符串值作为键,完全在 C 中处理该dict.__getitem__()方法,并且GIL 永远不会解锁,从而使查找线程安全。dict[key]

注意那里的资格;如果您创建一个defaultdict具有不同默认值工厂的实例,一个使用 Python 代码(lambda: [1, 2, 3]例如)的实例,那么所有的赌注都将被取消,因为这意味着 C 代码回调到 Python 代码中,并且 GIL 可以在执行字节码时再次释放lambda功能。这同样适用于键,当使用在 Python 代码中实现的对象时,__hash__可以__eq__在那里进行线程切换。接下来,如果工厂是用显式释放 GIL 的 C 代码编写的,则可能会发生线程切换,并且线程安全性已被排除在外。

于 2013-07-16T16:54:06.313 回答