使用subprocess模块会释放python GIL吗?

问题描述 投票:0回答:3

当通过Python的

subprocess
模块调用需要相对较长时间的linux二进制文件时,这会释放GIL吗?

我想并行化一些从命令行调用二进制程序的代码。使用线程(通过

threading
multiprocessing.pool.ThreadPool
)还是
multiprocessing
更好?我的假设是,如果
subprocess
释放 GIL,那么选择
threading
选项会更好。

python multithreading subprocess python-multithreading gil
3个回答
17
投票

当通过Python的

subprocess
模块调用需要相对较长时间的linux二进制文件时,这会释放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)来进行测试。


7
投票

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()

您可以使用

multiprocessing.ThreadPool
来限制同时运行的程序数量


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

只需确保您不调用任何 wait 等待进程完成其工作的方法(例如 Popen.communicate())以避免 Python 脚本阻塞。

正如这个答案

中提到的

multiprocessing
用于在现有的 (Python) 代码支持更灵活的通信 进程族。
multiprocessing
模块旨在提供 接口和功能与线程非常相似,而 允许 CPython 在多个 CPU/核心之间扩展处理 尽管有 GIL。

因此,考虑到您的用例,

subprocess
似乎是正确的选择。

© www.soinside.com 2019 - 2024. All rights reserved.