今天我在该模块文档的“注意事项”部分看到了这个有趣的声明thread:
thread
并非所有可能阻塞等待 I/O 的内置函数都允许其他线程运行。(最流行的函数(time.sleep()、file.read()、 select.select())可按预期工作。)
time.sleep()
file.read()
select.select()
在我见过的几乎所有讨论 Python 线程的地方,人们总是假设所有执行 I/O 的内置函数都会释放 GIL,这意味着其他线程可以在函数阻塞时运行。据我所知,I/O 操作阻塞其他线程的唯一风险是,如果该操作是在有缺陷的 C 扩展上进行的,而忽略了释放 GIL。
那么,文档中的这个说法thread真的正确吗?是否有任何内置的阻塞 I/O 操作不会释放 GIL?到目前为止,我还没有找到任何具体的例子。
这是一个有趣的问题,Python 文档中关于这个问题的表述thread确实既令人好奇又含糊。让我们来分析一下情况。
在 CPython 中,I/O 密集型操作通常会释放全局解释器锁 (GIL),以便其他线程可以在阻塞操作(如文件读取或网络 I/O)完成时执行。这对于大多数标准库的 I/O 函数来说都是典型的,并且适用于使用固有释放 GIL 的系统调用的函数,例如time.sleep()、select.select()和大多数file操作(read()、write()等)。
file
read()
write()
然而,有几个细微差别需要考虑:
open
read
write
socket
文档的声明是对潜在边缘情况的警告,但没有提供具体示例,因为这些情况可能很少见且特定于平台。通常,Python 标准库中的标准 I/O 函数的实现是为了适当地释放 GIL 以进行 I/O 绑定操作。
如果您很好奇,可以测试特定函数在实际中是否会阻塞其他线程。您可以使用一个简单的多线程测试,其中一个线程执行疑似阻塞操作,另一个线程并行运行,检查延迟。但是,可以安全地假设,对于大多数常见的 I/O 函数,GIL 将按预期释放,特别是在 Linux 和 macOS 等经过充分测试的平台上。