1

我有一个在 Python / PyQt / QtWebKit 基础上运行的 JavaScript 应用程序,它创建subprocess.Popen对象来运行外部进程。

Popen对象保存在字典中并由内部标识符引用,以便 JS 应用程序可以Popen通过 a 调用 的方法pyqtSlotpoll()以确定进程是否仍在运行或kill()杀死流氓进程。

如果一个进程不再运行,我想Popen从字典中删除它的对象以进行垃圾收集。

自动清理字典以防止内存泄漏的推荐方法是什么?

到目前为止我的想法:

  • 在每个生成的进程中调用Popen.wait()一个线程以在终止时立即执行自动清理。
    PRO:立即清理,线程可能不会消耗太多 CPU 功率,因为​​它们应该处于睡眠状态,对吧?
    CON:许多线程取决于生成活动。
  • 使用线程调用Popen.poll()所有现有进程并检查returncode它们是否已终止并在这种情况下进行清理。
    PRO:所有进程只有一个工作线程,内存使用量更低。
    CON:需要定期轮询,如果有许多长时间运行的进程或产生大量已处理的进程,则 CPU 使用率会更高。

你会选择哪一个,为什么?还是有更好的解决方案?

4

1 回答 1

3

对于与平台无关的解决方案,我会选择选项 #2,因为高 CPU 使用率的“CON”可以通过类似...

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,您应该可以通过as使用该WaitForMultipleObjects功能,尽管您必须研究其可行性。ctypesctypes.windll.kernel32.WaitForMultipleObjects

对于 OSX 和 Linux,使用模块SIGCHLD异步处理可能是最简单的。signal

一个快速n'肮脏的例子......

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'
于 2013-05-02T15:24:41.603 回答