2013-05-02 90 views
1

我有一個Python運行的JavaScript應用程序/ PyQt的/ QtWebKit的基礎,它創建subprocess.Popen對象運行外部進程。如何清理subprocess.Popen實例在進程終止

Popen對象都保存在一個字典和引用的通過內部標識符,使得JS應用程序可以調用Popen的經由諸如poll()pyqtSlot方法來確定過程是否仍在運行或kill()殺死流氓過程。

如果一個進程沒有任何更多的運行,我想從字典垃圾收集刪除其Popen對象。

什麼是推薦的方法來自動清理字典以防止內存泄漏?

我的想法而已:

  • 呼叫Popen.wait()每催生進程中的線程權終止時執行自動清理。
    PRO:立即清理,線程可能不費多少CPU功率,因爲​​他們應該睡覺了吧?
    CON:許多線程取決於產卵活動。
  • 使用一個線程來呼籲所有現有流程Popen.poll(),並檢查returncode他們是否終止,在此情況下清理。
    PRO:所有進程只需一個工作線程,內存使用率更低。
    CON:必要定期輪詢,如果有許多長時間運行的過程或批次的處理產生了更高的CPU使用率。

你會選擇哪一個,爲什麼?或者有更好的解決方案

+0

什麼操作系統[S]將在需要工作嗎? – Aya 2013-05-02 14:58:20

+0

如果可能的話,主要是Windows,Mac OS X,Linux會很好。最好的將是一個平臺不可知的解決方案。 – Archimedix 2013-05-02 15:53:32

+0

那麼,我給出的答案將適用於Linux和OSX。我將不得不考慮一下Windows解決方案。 – Aya 2013-05-02 15:55:00

回答

2

對於平臺無關的解決方案,我會去與選項#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,您應該能夠通過ctypes作爲ctypes.windll.kernel32.WaitForMultipleObjects使用WaitForMultipleObjects函數,但您必須查看可行性。

對於OSX和Linux,使用signal模塊異步處理SIGCHLD可能是最容易的。

一個快速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' 
+0

是啊,我一直在思考使用'WaitForMultipleObjects的()',但是該解決方案將是一個有點複雜我想...你可能不得不在每次添加一個新的處理時間,這也許是更新等待進程不值得付出努力,例如在一個循環中,並使用幾秒鐘的等待超時。此外,由於MAXIMUM_WAIT_OBJECTS限制,您可能需要將等待分成多個線程。 – Archimedix 2013-05-03 08:27:14

+0

@Archimedix是的。這與在多個文件描述符上使用['select()'](http://docs.python.org/2/library/select.html#select.select)非常相似 - 通常的習慣用法是包含FD(通常一個監聽套接字),這可能會改變你正在監視的FD集。所以在你的情況下,你需要在集合中包含一些可用於檢測何時創建新進程的對象,然後等待超時時間可能很長。但唯一的好處就是消除選項#2的延遲。 (接下來的評論) – Aya 2013-05-03 13:07:20

+0

@Archimedix(續前一評論)'SIGCHLD'解決方案似乎是最優雅的,是異步的(即不需要阻塞調用),並且可以在主線程中使用。它也可以在Windows上使用,只要你的代碼可以在針對[cygwin](http://www.cygwin.com/)編譯的Python版本下運行,但是如果你的代碼是'重新使用幾個第三方Python擴展模塊。我現在建議使用選項#2,因爲它不需要太多的線程管理,如果有必要的話,可以在稍後進行優化。 – Aya 2013-05-03 13:20:34