Python多线程和GIL

6
我在阅读关于GIL的内容时发现它从未明确指出是否包括主线程(我认为是的)。我提问的原因是因为我有一个设置了线程以修改字典的程序。主线程根据玩家输入添加/删除,而一个线程循环数据更新和更改数据。
然而,在某些情况下,一个线程可能会迭代字典键,其中一个线程可能会删除它们。如果有所谓的GIL并且它们按顺序运行,为什么我会收到“dict changed”错误?如果只有一个线程可以同时运行,那么这种情况理论上不应该发生。
能否有人解释一下这样的情况?谢谢你。

2
通常情况下,Python线程只对I/O绑定的线程有意义。如果您想要在并行访问数据结构时保持一致性,则需要显式锁定。如果您需要CPU绑定的并行性,则通常必须使用完全不同的东西。 - 9000
CPU密集型任务应使用多进程而非线程来运行。使用消息队列作为任务队列,让许多工作人员拉取任务(或将它们推送)是Python并行性的一种场景。 - JerodG
4个回答

11

它们同时运行,只是不会同时执行。迭代可能会交替进行。引用 Python:

CPython解释器使用的机制,确保一次只有一个线程在执行Python bytecode。

因此,两个for循环可以同时运行,只是没有(例如)同时出现两个del dict[index]


6

GIL(全局解释器锁)在Python的字节码级别上进行锁定,适用于所有线程,即使是主线程。如果一个线程修改一个字典,另一个线程迭代键,它们将互相干扰。

“同一时间只有一个线程运行”是正确的,但你必须理解粒度的单位。在CPython的GIL中,粒度是一个字节码指令,因此执行可以在任何字节码处在不同线程之间切换。


GIL是CPython解释器使用的机制,用于确保一次只有一个线程执行Python字节码。这简化了CPython的实现,使对象模型(包括关键内置类型,如dict)隐式地安全防止并发访问。如果我没错的话,这意味着dict是线程安全的? - boh
1
这取决于您对字典执行的操作。d[k] += 1不是线程安全的。如果有疑问,请使用自己的同步。 - Ned Batchelder

3

gil(全局解释器锁)可以防止两个线程同时修改解释器状态。它并不提供任何线程一致性约束,也不提供粒度小于整个进程的任何互斥机制。如果您需要在两个线程中读取和修改字典,则应使用互斥锁。


1

Python切换线程的频率比你想象的要高。你说“只有一个”线程应该在同一时间运行,从技术上讲这是正确的,但它取决于你对“一个”的定义。Python的原子操作非常小。例如:向字典中添加单个项目。遍历整个字典可能会被中断。

您应该使用threading库中的锁对象来隔离程序的原子操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接