Python子进程引用导致fd耗尽

时间:2014-05-20 08:28:47

标签: python list reference subprocess python-multithreading

关于这篇文章:Python del Statement

我最近遇到了以下代码段:

# custom_process.py

import threading
import subprocess

myList = []  # module-wide list

class Foo(threading.Thread):

    myprocess = None     
    returncode = None

    def run(self):
        self.myprocess = subprocess.Popen(...)

        global myList
        myList.append(self.myprocess)

        ...  # Code skipped for brevity

        self.returncode = self.myprocess.returncode
        tmp1, tmp2 = self.myprocess.communicate()

        ...  # Code skipped for brevity

        del self.myprocess

此代码在连续调用Foo的{​​{1}}方法时,耗尽系统上的可用文件描述符会抛出异常:run

因此我想知道......在处理子进程对象时,文件描述符是否与实际的OS进程一起关闭,或者当Python子进程对象的引用计数变为零时?

提前致谢。

5 个答案:

答案 0 :(得分:2)

是的,del只会移除对象的引用,它的名称将会如此。要从列表中删除项目,您需要使用不同的语法。

>>> a = 10 
>>> l = [a]
>>> del a
>>> a                  # the name a will be gone now
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  NameError: name 'a' is not defined
>>> l                  # but the list will still contain 10
[10]
>>> del l[0]
>>> l                  # but now it is gone
[]
>>> 

或者,您可以致电l.remove(a)

答案 1 :(得分:1)

Popen启动一个新的OS进程,并返回一个可用于与之交互的python对象。

这两个仍然是分开的,删除python对象并不一定会影响单独的运行过程,它只是删除了一种方便的与它交互的方式。

对于python Popen对象在进程终止后留下来是完全合理的,反之亦然。

行为将取决于Popen的论据,例如,即使整个脚本终止,也可以将进程配置为继续运行。

如果是一次性过程,它应该在communicate()调用后终止,但是您应该在此次调用后检查返回代码以进行验证(或使用poll()检查是否& #39; s活着)。

也可能需要一段时间才能终止,如果你是每秒调用此函数1000次,你可能比他们终止更快地启动它们,导致FD耗尽。

答案 2 :(得分:1)

我发现了一篇很好的博客文章,描述了我所面临的情况:

How subprocess and file descriptors work in Python

通过强调* nixes和Windows之间的文件处理差异,直观地解决问题。作者甚至提出了一个解决方案!绝对值得一读。

答案 3 :(得分:0)

在保留进程列表的同时避免fd耗尽的最佳方法是使用进程池。这样,您可以限制同时打开的进程数,只有在前一个进程结束时才会启动新进程。

https://docs.python.org/3.4/library/multiprocessing.html#using-a-pool-of-workers

当然,其他两个答案都是关于引用删除以及为什么会出现内存耗尽的原因,这就是为什么我不在这里重复它。

答案 4 :(得分:0)

您可以使用 myprocess.kill() myprocess.terminate()来关闭正在运行的进程 - 如果您在退出时不需要它run()方法。 (你的片段意味着你没有)

如果您也不将其添加为属性并且不将其添加到 myList (通过您的代码,它是多余的),它将在您退出后立即销毁run() - 在Python中,你通常依靠垃圾收集器而实际使用 del 运算符很少