python - GIL 如何处理分块 I/O 读/写?

标签 python python-3.x

假设我有一个 io.BytesIO() 我想写一个对坐在线程上的响应:

f = io.ByteIO()
with requests.Session() as s:
    r = s.get(url, stream = True)
    for chunk in r.iter_content(chunk_size = 1024):
        f.write(chunk)

现在这不是在硬盘上而是在内存中(为了我的目的得到了很多),所以我不必担心针头会成为瓶颈。我知道对于阻塞 I/O(文件读/写),GIL 从 docs 中释放。而这个 SO post由 Alex Martelli 编写,但我想知道,GIL 是否只是在 f.write() 上释放,然后在循环的 __next__() 调用上重新获取?

所以我最终得到的是一堆快速的 GIL 获取和发布。显然,我必须计时才能确定任何值得注意的事情,但是在多线程网络抓取程序上写入内存文件对象通常是否支持 GIL 绕过

如果没有,我将只处理大量响应并将它们转储到队列中并在 __main__ 上处理。

最佳答案

根据我在 BytesIO type's source code 中看到的内容,在调用 BytesIO.write 期间不会释放 GIL,因为它只是在进行快速内存复制。只有对于可能阻塞的系统调用,释放 GIL 才有意义。

r.iter_content 生成器的__next__ 方法中可能有这样一个系统调用(当从套接字读取数据时),但在写入端没有.

但我认为您的问题反射(reflect)了对内置函数在执行阻塞操作时释放 GIL 意味着什么的错误理解。它会在执行可能阻塞的系统调用之前释放 GIL。但它会在返回 Python 代码之前重新获取 GIL。所以不管你在一个循环中有多少这样的 GIL 释放操作,所有涉及的 Python 代码都将在持有 GIL 的情况下运行。 GIL 永远不会被一个操作释放并被另一个操作回收。它作为一个独立的步骤针对每个操作进行释放和回收。

例如,您可以查看 the C code that implements writing to a file descriptor .宏 Py_BEGIN_ALLOW_THREADS 释放 GIL。几行之后,Py_END_ALLOW_THREADS 重新获取 GIL。在这些步骤之间没有运行 Python 级别,只有一些关于 errno 的低级 C 分配,以及可能阻塞的 write 系统调用,在磁盘上等待。

关于python - GIL 如何处理分块 I/O 读/写?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50123928/

相关文章:

python - 测试 Flask 应用程序内部是否运行过 'flask db init'

python - 字符串列表到整数数组

python - 仅使用静态方法调用继承类的构造函数

python - AssertionError : invalid dtype determination in get_concat_dtype when concatenating operation on list of Dataframes的解决方法

python - 提取 JSON 元素

python 2.7 到 python 3.2 字符串格式错误

python-3.x - 模块未找到错误 : No module named 'kaggle.competitions'

python - 列表中元组值之间的关系根据它们的位置

python-3.x - 如何使 cv2.videoCapture.read() 更快?

python - Django/mysqlclient 寻找丢失的 `libmysqlclient.18.dylib` 文件