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

18 投票
3 回答
7136 浏览
提问于 2025-04-18 04:46

当通过Python的subprocess模块调用一个运行时间比较长的Linux程序时,这个过程会释放全局解释器锁(GIL)吗?

我想要并行处理一些代码,这些代码会从命令行调用一个程序。使用线程(通过threadingmultiprocessing.pool.ThreadPool)还是multiprocessing更好呢?我假设如果subprocess释放了GIL,那么选择threading的方式会更好。

3 个回答

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

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 来限制同时运行的程序数量

17

在通过Python的subprocess模块调用一个运行时间比较长的Linux程序时,这会释放全局解释器锁(GIL)吗?

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

你可能知道,在POSIX平台上,subprocess模块提供了一些方便的接口,这些接口是基于更底层的功能,比如forkexecvewaitpid

根据对CPython 2.7.9源代码的检查,forkexecve这两个函数并不会释放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,来测试这一点。

撰写回答