2008-09-19 15 views
21

Wie warten auf mehrere untergeordnete Prozesse in Python unter Windows, ohne aktive Wartezeit (Polling)? So etwas wie diese fast Werke für mich:Python unter Windows - Wie man auf mehrere untergeordnete Prozesse wartet?

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

Das Problem ist, dass, wenn proc2 Oberflächen vor proc1, der übergeordneten Prozess noch proc1 warten. Unter Unix würde man waitpid(0) in einer Schleife verwenden, um die Rückkehrcodes der untergeordneten Prozesse zu erhalten, wenn sie fertig sind - wie erreicht man so etwas in Python unter Windows?

+0

können Sie beschreiben die waitpid (0) Sie auf Unix verwenden würde? –

+2

http://docs.python.org/library/os.html#os.waitpid 'waitpid (0)' auf Unix wartet (außer 'WNOHANG' ist in der Option) für einen verfügbaren Kindstatus und gibt' (processid , Status) 'Tupel. –

Antwort

13

Es könnte übertrieben erscheinen, aber hier geht es:

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

Nun, wenn der Aufruf keine Parallelität unterstützt, dann muss man es außen implementieren :-) Danke! –

+2

Ich denke, es gibt einen kleinen Fehler im Beispielskript, ich sehe keine Aussage der Art: process_count - = 1 Ist nicht die letzte "während" eine Endlosschleife? –

+0

@ Ricardo Reyes: Ja, Kopieren und Einfügen war unvollständig. Vielen Dank. – tzot

5

Verdreht eine asynchronous process-spawning API hat, die unter Windows funktioniert. Es gibt tatsächlich mehrere verschiedene Implementierungen, von denen viele nicht so toll sind, aber Sie können zwischen ihnen wechseln, ohne Ihren Code zu ändern.

4

Verdreht unter Windows wird eine aktive Wartezeit unter der Decke führen. Wenn Sie keine Threads verwenden möchten, müssen Sie die win32-API verwenden, um das Abrufen zu vermeiden. Etwas wie folgt aus:

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) 
+0

Sie haben sich geirrt, dass die Thread-Lösung eine aktive Wartezeit ausführt. Ich weiß nichts über Twisted. –

5

Aufbauend auf zseil Antwort, können Sie dies mit einer Mischung aus subprocess und win32-API-Aufrufe. Ich habe gerade Ctypes verwendet, weil mein Python nicht win32api installiert hat. Ich habe hier nur als Beispiel sleep.exe von MSYS erstellt, aber klar, Sie könnten jeden beliebigen Prozess spawnen. Ich benutze OpenProcess(), um ein HANDLE von der Prozess-PID zu erhalten, und dann WaitForMultipleObjects, um darauf zu warten, dass ein Prozess beendet wird.

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

Sie können psutil verwenden:

>>> 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) 

‚gegangen‘ und ‚lebendig‘ sind Listen angibt, welche Prozesse gegangen sind und welche sind noch am Leben.

Optional können Sie einen Rückruf angeben, die jedes Mal, wenn einer der überwachten Prozesse beendet aufgerufen wird:

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