当通过Python的subprocess
模块调用需要相对较长时间的Linux二进制文件时,是否会释放GIL?
我想并行化一些从命令行调用二进制程序的代码。使用线程(通过threading
和multiprocessing.pool.ThreadPool
)或multiprocessing
会更好吗?我的假设是,如果subprocess
释放GIL,那么选择threading
选项会更好。
答案 0 :(得分:14)
当通过Python的
subprocess
模块调用需要相对较长时间的Linux二进制文件时,是否会释放GIL?
是的,它会在调用过程中释放Global Interpreter Lock (GIL)。
正如您可能知道的那样,在POSIX平台上,subprocess
在" raw"上提供了便利界面。来自fork
,execve
和waitpid
的组件。
通过检查CPython 2.7.9源,fork
和execve
执行而不是释放GIL。但是,这些调用不会阻止,因此我们不希望释放GIL。
waitpid
当然会阻止,但我们看到它的实现确实使用ALLOW_THREADS宏放弃了GIL:
static PyObject *
posix_waitpid(PyObject *self, PyObject *args)
{
....
Py_BEGIN_ALLOW_THREADS
pid = waitpid(pid, &status, options);
Py_END_ALLOW_THREADS
....
这也可以通过从演示多线程python脚本调用一些长期运行的程序(如sleep来测试。
答案 1 :(得分:5)
GIL并不跨越多个流程。 subprocess.Popen
开始一个新流程。如果它启动Python进程,那么它将拥有自己的GIL。
如果您只想并行运行某些Linux二进制文件,那么您不需要多个线程(或multiprocessing
创建的进程):
from subprocess import Popen
# start all processes
processes = [Popen(['program', str(i)]) for i in range(10)]
# now all processes run in parallel
# wait for processes to complete
for p in processes:
p.wait()
你可以use multiprocessing.ThreadPool
to limit number of concurrently run programs。
答案 2 :(得分:1)
由于subprocess
用于运行可执行文件(它本质上是os.fork()
和os.execve()
的包装器),因此使用它可能更有意义。您可以使用subprocess.Popen
。类似的东西:
import subprocess
process = subprocess.Popen(["binary"])
这将作为一个单独的进程运行,因此不受GIL的影响。然后,您可以使用Popen.poll()
方法检查子进程是否已终止:
if process.poll():
# process has finished its work
returncode = process.returncode
只需要确保不要调用等待的任何方法来完成其工作(例如Popen.communicate())以避免Python脚本阻塞。
中所述
multiprocessing
用于在现有内部运行功能 (Python)代码,支持更灵活的通信 流程系列。multiprocessing
模块旨在提供 接口和功能非常类似于线程 允许CPython在多个CPU /核心之间扩展您的处理 尽管有GIL。
因此,考虑到您的用例,subprocess
似乎是正确的选择。