2008-09-19 50 views
21

如何在Windows上等待Python中的多個子進程,而無需主動等待(輪詢)?像這樣的事情幾乎作品我:Windows上的Python - 如何等待多個子進程?

proc1 = subprocess.Popen(['python','mytest.py']) 
proc2 = subprocess.Popen(['python','mytest.py'])  
proc1.wait() 
print "1 finished" 
proc2.wait() 
print "2 finished" 

的問題是,當proc1之前proc2結束,父進程將仍然等待proc1。在Unix上,一個循環中會使用waitpid(0)來完成子進程的返回代碼 - 如何在Windows上的Python中實現這樣的功能?

+0

你可以描述waitpid函數(0)你在Unix上使用? – 2010-01-27 19:55:58

+2

http://docs.python.org/library/os.html#os.waitpid UNIX上的waitpid(0)對於任何可用的子狀態等待(除非`WNOHANG在選項中)並返回`(processid ,狀態)`元組。 – 2010-01-28 08:29:00

回答

13

這可能似乎矯枉過正,但是,這裏有雲:

import Queue, thread, subprocess 

results= Queue.Queue() 
def process_waiter(popen, description, que): 
    try: popen.wait() 
    finally: que.put((description, popen.returncode)) 
process_count= 0 

proc1= subprocess.Popen(['python', 'mytest.py']) 
thread.start_new_thread(process_waiter, 
    (proc1, "1 finished", results)) 
process_count+= 1 

proc2= subprocess.Popen(['python', 'mytest.py']) 
thread.start_new_thread(process_waiter, 
    (proc2, "2 finished", results)) 
process_count+= 1 

# etc 

while process_count > 0: 
    description, rc= results.get() 
    print "job", description, "ended with rc =", rc 
    process_count-= 1 
+0

那麼,如果調用不支持並行性,那麼必須在外部實現它:-)謝謝! – 2008-09-19 10:16:44

5

雙絞線有asynchronous process-spawning API它在Windows上運行。實際上有幾種不同的實現方式,其中很多不是很好,但可以在不更改代碼的情況下在它們之間切換。

4

在Windows上扭曲將在封面下執行一個主動等待。如果你不想使用線程,你將不得不使用win32 API來避免輪詢。這樣的事情:

import win32process 
import win32event 

# Note: CreateProcess() args are somewhat cryptic, look them up on MSDN 
proc1, thread1, pid1, tid1 = win32process.CreateProcess(...) 
proc2, thread2, pid2, tid2 = win32process.CreateProcess(...) 
thread1.close() 
thread2.close() 

processes = {proc1: "proc1", proc2: "proc2"} 

while processes: 
    handles = processes.keys() 
    # Note: WaitForMultipleObjects() supports at most 64 processes at a time 
    index = win32event.WaitForMultipleObjects(handles, False, win32event.INFINITE) 
    finished = handles[index] 
    exitcode = win32process.GetExitCodeProcess(finished) 
    procname = processes.pop(finished) 
    finished.close() 
    print "Subprocess %s finished with exit code %d" % (procname, exitcode) 
5

基於zseil的答案,你可以做到這一點與子進程和win32 API調用混合。我使用了直接的ctypes,因爲我的Python沒有安裝win32api。我只是在這裏以MSYS產生sleep.exe爲例,但顯然你可以產生你喜歡的任何進程。我使用OpenProcess()從進程的PID中獲取HANDLE,然後WaitForMultipleObjects等待任何進程完成。

import ctypes, subprocess 
from random import randint 
SYNCHRONIZE=0x00100000 
INFINITE = -1 
numprocs = 5 
handles = {} 

for i in xrange(numprocs): 
    sleeptime = randint(5,10) 
    p = subprocess.Popen([r"c:\msys\1.0\bin\sleep.exe", str(sleeptime)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False) 
    h = ctypes.windll.kernel32.OpenProcess(SYNCHRONIZE, False, p.pid) 
    handles[h] = p.pid 
    print "Spawned Process %d" % p.pid 

while len(handles) > 0: 
    print "Waiting for %d children..." % len(handles) 
    arrtype = ctypes.c_long * len(handles) 
    handle_array = arrtype(*handles.keys()) 
    ret = ctypes.windll.kernel32.WaitForMultipleObjects(len(handle_array), handle_array, False, INFINITE) 
    h = handle_array[ret] 
    ctypes.windll.kernel32.CloseHandle(h) 
    print "Process %d done" % handles[h] 
    del handles[h] 
print "All done!" 
2

您可以使用psutil

>>> import subprocess 
>>> import psutil 
>>> 
>>> proc1 = subprocess.Popen(['python','mytest.py']) 
>>> proc2 = subprocess.Popen(['python','mytest.py'])  
>>> ls = [psutil.Process(proc1.pid), psutil.Process(proc2.pid)] 
>>> 
>>> gone, alive = psutil.wait_procs(ls, timeout=3) 

「水漲船高」和「活着」是列表表明哪些進程都消失了,哪些還活着。

可選方案,您可以指定被調用每次觀看過程中的一個終止的回調:

>>> def on_terminate(proc): 
...  print "%s terminated" % proc 
... 
>>> gone, alive = psutil.wait_procs(ls, timeout=3, callback=on_terminate)