假设我有一个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和post释放的,但是我想知道,GIL是在f.write()
上释放,然后在__next__()
循环调用上重新获取的吗?你知道吗
所以我最终得到的是一堆快速的GIL收购和发行。很明显,我必须计时以确定任何值得注意的事情,但是在多线程web scraper上写入内存中的文件对象通常支持GIL绕过?你知道吗
如果没有,我将只处理大型响应并将它们转储到队列中,然后在__main__
上进行处理。你知道吗
从^{} type's source code 中可以看到,GIL在调用
BytesIO.write
期间没有被释放,因为它只是做一个快速内存拷贝。只有系统调用可能会阻止GIL的释放才有意义。你知道吗在
r.iter_content
生成器的__next__
方法中可能有这样一个系统调用(当从套接字读取数据时),但在写端没有。你知道吗但我认为你的问题反映了一种错误的理解,即在执行阻塞操作时,对于一个内置函数释放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
系统调用在磁盘上等待。你知道吗相关问题 更多 >
编程相关推荐