16

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

我想并行化一些从命令行调用二进制程序的代码。使用线程(通过threading和 a multiprocessing.pool.ThreadPool)还是更好multiprocessing?我的假设是,如果subprocess发布 GIL,那么选择该threading选项会更好。

4

3 回答 3

16

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

是的,它在调用过程中释放全局解释器锁(GIL) 。

您可能知道,在 POSIX 平台上,subprocess在来自forkexecvewaitpid.

通过检查 CPython 2.7.9 源代码,不要fork发布execveGIL 。但是,这些调用不会阻塞,所以我们不希望 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 )来测试。

于 2015-05-11T22:10:43.907 回答
6

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

您可以使用multiprocessing.ThreadPool限制并发运行程序的数量

于 2014-04-29T16:05:32.603 回答
1

由于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这似乎是正确的选择。

于 2014-04-29T16:08:13.867 回答