根据我在研究 Python GIL 时的理解,一次只能执行一个线程(谁持有锁)。然而,如果这是真的,那么为什么这段代码只需要 3 秒的执行时间,而不是 15 秒呢?
import threading
import time
def worker():
"""thread worker function"""
time.sleep(3)
print 'Worker'
for i in range(5):
t = threading.Thread(target=worker)
t.start()
Run Code Online (Sandbox Code Playgroud)
根据对线程的直觉,我原以为这需要 3 秒,但确实如此。但是在了解了 GIL 并且可以立即执行一个线程之后,现在我看这段代码并想,为什么不花 15 秒?
time.sleep(3)不等于循环运行 3 秒,在持有 GIL 锁的同时消耗 CPU 周期。该线程被切换 3 秒,允许其他线程运行。
请参阅此 stackoverflow 问题,了解为什么time.sleep不是 CPU 密集型操作。总而言之,线程被挂起,直到超时。
因为所有线程都在休眠,所以 CPU 上没有进行任何实际工作,并且 GIL 锁不会禁止这些线程同时工作,因为一开始就没有持有持久锁。
当您有多个线程执行 CPU 密集型工作时,需要考虑 GIL 锁。在这种情况下,您会看到预期的放缓。GIL 不会导致纯 IO 密集型工作的任何减慢。
马里奥的回答是一个很好的高级回答。如果您对如何实现的一些细节感兴趣:在 CPython 中, 的实现用/time.sleep包装其select系统调用:Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS
这些是在释放/获取 GIL 的同时保存和恢复线程状态的宏:
https://github.com/python/cpython/blob/7c59d7c9860cdbaf4a9c26c9142aebd3259d046e/Include/ceval.h#L86-L94 https://github.com/python/cpython/blob/4c9ea093cd752a656737352a65687353c752a65687353c752a65683737353c85c88c88c88c