为什么 time.sleep(...) 不受 GIL 的影响?

use*_*651 5 python gil

根据我在研究 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 秒?

Mar*_*hac 8

time.sleep(3)不等于循环运行 3 秒,在持有 GIL 锁的同时消耗 CPU 周期。该线程被切换 3 秒,允许其他线程运行。

请参阅此 stackoverflow 问题,了解为什么time.sleep不是 CPU 密集型操作。总而言之,线程被挂起,直到超时。

因为所有线程都在休眠,所以 CPU 上没有进行任何实际工作,并且 GIL 锁不会禁止这些线程同时工作,因为一开始就没有持有持久锁。

当您有多个线程执行 CPU 密集型工作时,需要考虑 GIL 锁。在这种情况下,您会看到预期的放缓。GIL 不会导致纯 IO 密集型工作的任何减慢。


jtb*_*des 5

马里奥的回答是一个很好的高级回答。如果您对如何实现的一些细节感兴趣:在 CPython 中, 的实现用/time.sleep包装其select系统调用:Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS

https://github.com/python/cpython/blob/7ba1f75f3f02b4b50ac6d7e17d15e467afa36aac/Modules/timemodule.c#L1880-L1882

这些是在释放/获取 GIL 的同时保存和恢复线程状态的宏:

https://github.com/python/cpython/blob/7c59d7c9860cdbaf4a9c26c9142aebd3259d046e/Include/ceval.h#L86-L94 https://github.com/python/cpython/blob/4c9ea093cd752a656737352a65687353c752a65687353c752a65683737353c85c88c88c88c