【问题标题】:Why is a Python I/O bound task not blocked by the GIL?为什么 GIL 不会阻止 Python I/O 绑定任务?
【发布时间】:2015-05-30 00:26:20
【问题描述】:

python threading 文档指出“......线程仍然是一个合适的模型 如果你想同时运行多个 I/O-bound 任务", 显然是因为 I/O-bound 进程可以避免阻止 来自 CPU 密集型任务中并发执行的线程。

但我不明白的是,I/O 任务仍然使用 CPU。所以 怎么可能不遇到同样的问题?是不是因为 I/O 绑定任务不需要内存管理?

【问题讨论】:

  • "I/O 任务仍然使用 CPU" ... 通常情况下,情况并非如此。许多/大多数现代外设都可以直接访问内存,而不是 PIO,而不需要 CPU 周期来移动数据。

标签: python multithreading


【解决方案1】:

Python 的所有阻塞 I/O 原语都会在等待 I/O 块解析时释放 GIL —— 就是这么简单!他们当然需要在继续执行进一步的 Python 代码之前再次获取 GIL,但是对于他们只是等待一些 I/O 系统调用的长期机器周期间隔,他们不需要'不需要 GIL,所以他们不会坚持下去!

【讨论】:

  • @Shashank,不——我有“自然编码”Python 与 C++ I/O 绑定代码的基准测试,Python 绝对围绕 C++ 运行——快两三倍。删除 GIL 总是非常快,并且再次获取它非常快,除非您有更多对您有好处的活动线程!
  • (对于 IO,我遇到的最大瓶颈通常是不合适的缓冲区和重复的“每个字节”系统 IO 调用。跨语言几乎通用;有些隐藏得更好/默认情况下。)
  • @Shashank 我不确定这与 GIL 的关系是否与输出缓冲有关。当处于“交互模式”时,每次打印的末尾都有一个隐含的“刷新”(感谢换行符)——至少更公平的比较会直接写入非交互流。 (向控制台写入许多行相对较慢,即使在“普通 C”中也是如此。)参见 stackoverflow.com/questions/1716296
  • @user2864740 你是对的,当写入非交互式流时,第一种方法的性能有很大的提升。然而,第二种方式仍然以明显的优势始终更快。
  • @Shashank 对于一个简单的案例,我可以相信,因为 1 个“上下文切换”比 N 快 - 并且 假设 并发性没有任何好处。在观看缓冲时,我的工作并没有受到这样的影响(阅读:如果有差异,我从来没有理由担心它)。
【解决方案2】:

GIL in CPython1与正在执行的 Python 代码有关。只要不需要与 Python 运行时交互,使用大量 CPU 的线程安全 C 扩展可能会释放 GIL。

只要 C 代码需要与 Python 进行“对话”(阅读:回调到 Python 运行时),它就需要再次获取 GIL - 也就是说,GIL 是为“建立保护/原子行为”解释器”(我用这个词很松散)并且不是来防止本机/非 Python 代码同时运行。

围绕 I/O 释放 GIL(阻塞与否,使用 CPU 或不使用 CPU)是一回事 - 直到将数据移入 Python,没有理由获取 GIL。 p>


1 GIL 是有争议的,因为它阻止了多线程 CPython 程序在某些情况下充分利用多处理器系统。请注意,潜在的阻塞或长时间运行的操作(例如 I/O、图像处理和 NumPy 数字运算)发生在 GIL 之外。因此只有在多线程程序中花费大量时间在 GIL 中,解释 CPython 字节码,GIL 才会成为瓶颈。

【讨论】:

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