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

时间:2018-05-01 21:15:23

标签: 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是由Alex Martelli从docs和SO post释放的,但我想知道,GIL是否刚刚在{{1}上发布然后重新获取循环的f.write()调用?

所以我最终得到的是一系列快速的GIL收购和发布。显然我不得不花时间来确定值得注意的事情,但是在多线程Web刮刀上写入内存文件对象一般都支持GIL绕过吗?

如果没有,我只会处理大型响应并将其转储到队列中并在__next__()处理。

1 个答案:

答案 0 :(得分:0)

根据我在BytesIO type's source code中看到的内容,GIL在调用emitEvent期间未发布,因为它只是快速进行内存复制。它仅适用于可能阻止释放GIL的系统调用。

BytesIO.write生成器的__next__方法中可能存在这样的系统调用(当从套接字读取数据时),但是在写入方面没有。

但我认为你的问题反映了对内置函数在执行阻塞操作时释放GIL意味着什么的错误理解。它会在它进行潜在阻塞系统调用之前释放GIL。但它会在返回Python代码之前重新获取GIL。因此,在循环中有多少这样的GIL释放操作并不重要,所涉及的所有Python代码都将在保持GIL的情况下运行。 GIL从未通过一次操作发布并由不同的操作回收。它作为一个独立的步骤发布和回收每个操作。

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