使用subprocess模块会释放Python GIL吗?
当通过Python的subprocess
模块调用一个运行时间比较长的Linux程序时,这个过程会释放全局解释器锁(GIL)吗?
我想要并行处理一些代码,这些代码会从命令行调用一个程序。使用线程(通过threading
和multiprocessing.pool.ThreadPool
)还是multiprocessing
更好呢?我假设如果subprocess
释放了GIL,那么选择threading
的方式会更好。
3 个回答
因为 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
似乎是正确的选择。
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()
在通过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,来测试这一点。