2008-09-19 20 views
21

¿Cómo se esperan procesos secundarios múltiples en Python en Windows, sin espera activa (sondeo)? Algo como esto casi funciona para mí:Python en Windows: ¿cómo esperar varios procesos secundarios?

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

El problema es que cuando proc2 acabados antes de proc1, el proceso padre se sigue esperar a proc1. En Unix se usaría waitpid(0) en un bucle para obtener los códigos de retorno de los procesos secundarios a medida que finalizan. ¿Cómo lograr algo como esto en Python en Windows?

+0

puede describir el waitpid (0) tendrá que utilizar en UNIX? –

+2

http://docs.python.org/library/os.html#os.waitpid 'waitpid (0)' en Unix espera (a menos que 'WNOHANG' esté en la opción) para cualquier estado hijo disponible y devuelve' (processid , estado) 'tupla. –

Respuesta

13

Podría parecer una exageración, pero, aquí va:

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

Bueno, si la llamada no admite el paralelismo, entonces hay que implementarlo fuera :-) ¡Gracias! –

+2

Creo que hay un error menor en el script de ejemplo, no veo ninguna declaración del tipo: process_count - = 1 ¿No es el último "while" un bucle infinito? –

+0

@Ricardo Reyes: sí, copiar y pegar estaba incompleto. Gracias. – tzot

5

trenzado tiene una asynchronous process-spawning API que funciona en Windows. En realidad, hay varias implementaciones diferentes, muchas de las cuales no son tan buenas, pero puede cambiar entre ellas sin cambiar su código.

4

Twisted en Windows llevará a cabo una espera activa bajo las sábanas. Si no desea utilizar subprocesos, deberá usar la API de win32 para evitar el sondeo. Algo como esto:

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

Está equivocado acerca de la solución enhebrada que realiza una espera activa. No sé sobre Twisted. –

5

Sobre la base de la respuesta de zseil, se puede hacer esto con una mezcla de subproceso y Win32 llamadas a la API. Usé ctypes rectos, porque mi Python no tiene instalado win32api. Solo estoy generando sleep.exe de MSYS aquí como un ejemplo, pero claramente podría generar cualquier proceso que desee. Uso OpenProcess() para obtener un MANGO del PID del proceso y luego WaitForMultipleObjects para esperar a que finalice cualquier proceso.

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

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

'desaparecido' y 'viva' son listas que indican qué procesos se han ido y cuáles aún están vivos.

Opcionalmente se puede especificar una devolución de llamada de la cual se invoca cada vez que uno de los procesos observados termina:

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