当通过Python的模块调用需要相对较长时间的linux二进制文件时subprocess
,这会释放GIL吗?
我想并行化一些从命令行调用二进制程序的代码。使用线程(通过threading
和 a multiprocessing.pool.ThreadPool
)还是更好multiprocessing
?我的假设是,如果subprocess
发布 GIL,那么选择该threading
选项会更好。
当通过Python的模块调用需要相对较长时间的linux二进制文件时subprocess
,这会释放GIL吗?
我想并行化一些从命令行调用二进制程序的代码。使用线程(通过threading
和 a multiprocessing.pool.ThreadPool
)还是更好multiprocessing
?我的假设是,如果subprocess
发布 GIL,那么选择该threading
选项会更好。
当通过Python的模块调用需要相对较长时间的linux二进制文件时
subprocess
,这会释放GIL吗?
是的,它在调用过程中释放全局解释器锁(GIL) 。
您可能知道,在 POSIX 平台上,subprocess
在来自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 )来测试。
GIL 不跨越多个进程。subprocess.Popen
开始一个新的过程。如果它启动一个 Python 进程,那么它将拥有自己的 GIL。
multiprocessing
如果您只想并行运行一些 linux 二进制文件,则不需要多个线程(或由 创建的进程):
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()
由于subprocess
用于运行可执行文件(它本质上是对os.fork()
and的包装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
这似乎是正确的选择。