Python 如何在进程终止时清理subprocess.Popen实例
我有一个JavaScript应用程序运行在Python /pyqt/qtWebKek基础上,创建了<代码>子进程。Popen < /Cord>对象运行外部进程。< /P>Python 如何在进程终止时清理subprocess.Popen实例,python,python-3.x,pyqt,subprocess,qtwebkit,Python,Python 3.x,Pyqt,Subprocess,Qtwebkit,我有一个JavaScript应用程序运行在Python /pyqt/qtWebKek基础上,创建了子进程。Popen < /Cord>对象运行外部进程。< /P> Popen对象保存在字典中,并由内部标识符引用,因此JS应用程序可以通过pyqtlot调用Popen的方法,例如poll()来确定进程是否仍在运行,或者kill()来杀死恶意进程 如果进程不再运行,我想从字典中删除它的Popen对象以进行垃圾收集 建议采用什么方法自动清理字典以防止内存泄漏 到目前为止,我的想法是: 在每个衍生进程的
Popen
对象保存在字典中,并由内部标识符引用,因此JS应用程序可以通过pyqtlot
调用Popen
的方法,例如poll()
来确定进程是否仍在运行,或者kill()
来杀死恶意进程
如果进程不再运行,我想从字典中删除它的Popen
对象以进行垃圾收集
建议采用什么方法自动清理字典以防止内存泄漏
到目前为止,我的想法是:
- 在每个衍生进程的线程中调用
,以便在终止时立即执行自动清理。Popen.wait()
PRO:即时清理,线程可能不会消耗太多CPU电量,因为它们应该处于休眠状态,对吗?
CON:许多线程取决于生成活动 - 使用线程调用所有现有进程上的
,并检查Popen.poll()
是否已终止并清除这些进程。returncode
PRO:所有进程只需一个工作线程,内存使用率更低。
CON:需要定期轮询,如果有许多长时间运行的进程或大量生成的已处理进程,则CPU使用率更高
你会选择哪一个?为什么?或者有更好的解决方案吗?对于平台无关的解决方案,我会选择选项2,因为高CPU使用率的“弊病”可以通过以下方式避免
import time
# Assuming the Popen objects are in the dictionary values
PROCESS_DICT = { ... }
def my_thread_main():
while 1:
dead_keys = []
for k, v in PROCESS_DICT.iteritems():
v.poll()
if v.returncode is not None:
dead_keys.append(k)
if not dead_keys:
time.sleep(1) # Adjust sleep time to taste
continue
for k in dead_keys:
del PROCESS_DICT[k]
…因此,如果迭代中没有进程死亡,您只需睡一会儿
因此,实际上,您的线程大部分时间仍处于睡眠状态,尽管子进程死亡和随后的“清理”之间存在潜在的延迟,但这并不是什么大问题,而且这应该比每个进程使用一个线程扩展得更好
不过,还有更好的平台相关解决方案
对于Windows,您应该能够通过asctypes.windell.kernel32.WaitForMultipleObjects
使用该函数,尽管您必须研究其可行性
对于OSX和Linux,可能最容易使用模块异步处理
一个肮脏的例子
import os
import time
import signal
import subprocess
# Map child PID to Popen object
SUBPROCESSES = {}
# Define handler
def handle_sigchld(signum, frame):
pid = os.wait()[0]
print 'Subprocess PID=%d ended' % pid
del SUBPROCESSES[pid]
# Handle SIGCHLD
signal.signal(signal.SIGCHLD, handle_sigchld)
# Spawn a couple of subprocesses
p1 = subprocess.Popen(['sleep', '1'])
SUBPROCESSES[p1.pid] = p1
p2 = subprocess.Popen(['sleep', '2'])
SUBPROCESSES[p2.pid] = p2
# Wait for all subprocesses to die
while SUBPROCESSES:
print 'tick'
time.sleep(1)
# Done
print 'All subprocesses died'
这需要在什么操作系统上工作?主要是Windows、Mac OS X,如果可能的话,最好是Linux。最好是平台无关的解决方案。好吧,我给出的答案将适用于Linux和OSX。我必须考虑一下Windows解决方案。好的,谢谢你,我忘了提到这是Python3.3.1更新版的答案。代码示例适用于Python2.x,但对于3.x-compat,您只需将
print foo
更改为print(foo)
。是的,我一直在考虑使用WaitForMultipleObjects()
,不过解决方案可能有点复杂。。。每次添加新进程时,您可能都必须更新等待进程,这可能不值得付出努力,例如,在循环中,使用几秒钟的等待超时或其他方式。此外,由于最大等待对象数的限制,您可能需要将等待分为多个线程。@Archimedix是的。这与在多个文件描述符上使用非常相似-通常的习惯用法是包含FD(通常是侦听套接字),这可能会更改您正在监视的FD集。因此,在您的情况下,您需要在集合中包含一些对象,这些对象可用于检测何时创建新进程,然后等待超时可能会很长。不过,唯一的好处是从选项2中删除延迟。(在下一篇评论中继续)@Archimedix(在上一篇评论中继续)SIGCHLD
解决方案似乎是最优雅的,它是异步的(即不需要阻塞调用),可以在主线程中使用。它也可以在Windows上使用,只要您的代码将在为编译的Python版本下运行,但如果您使用多个第三方Python扩展模块,这可能会更加复杂。我建议现在使用选项#2,因为它不需要太多的线程管理,如果有必要的话,以后再考虑优化。@Archimedix我刚刚遇到了一个使用WMI的异步Windows选项-但不确定适应Python有多容易。谢谢,我认为WMI太麻烦了。