【问题标题】:Why does time.sleep(...) not get affected by the GIL?为什么 time.sleep(...) 不受 GIL 影响?
【发布时间】:2020-05-15 00:54:25
【问题描述】:

根据我在研究 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()

根据对线程的直觉,我原以为这需要 3 秒,但确实如此。但是在了解了 GIL 并且可以一次执行一个线程之后,现在我看这段代码并想,为什么不需要 15 秒?

【问题讨论】:

  • 我认为sleep() 确实发布了 GIL。此外,Python 会强制每 N 毫秒或在执行 M 字节码指令后(取决于使用的 Python 版本)进行一次线程切换。
  • @martineau 如果这是真的,“等待”发生在哪里?
  • 我认为线程最终会并行等待。
  • 休眠线程没有执行。 5 个线程同时休眠不是同时执行。

标签: python gil


【解决方案1】:

马里奥的答案是一个很好的高级答案。如果您对如何实现的一些细节感兴趣:在 CPython 中,time.sleep 的实现将其select 系统调用与Py_BEGIN_ALLOW_THREADS / Py_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/4c9ea093cd752a6687864674d34250653653f743/Python/ceval.c#L498

【讨论】:

  • 在这里包含 CPython 源代码非常有趣,会给它一个很好的阅读欢呼。
【解决方案2】:

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

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

因为所有线程都处于休眠状态,所以 CPU 上没有真正的工作正在完成,而且 GIL 锁不会禁止这些线程同时工作,因为一开始就没有持有持久锁。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-10
    • 1970-01-01
    • 1970-01-01
    • 2020-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多