Pregunta

¿Cómo esperar varios procesos secundarios en Python en Windows, sin espera activa (sondeo)?Algo como esto casi funciona para mi:

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 termina antes proc1, el proceso principal seguirá esperando proc1.En Unix se usaría waitpid(0) en un bucle para obtener los códigos de retorno de los procesos secundarios cuando finalizan: ¿cómo lograr algo como esto en Python en Windows?

¿Fue útil?

Solución

Puede parecer excesivo, 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

Otros consejos

Twisted tiene un API de generación de procesos asíncronos que funciona en Windows.En realidad, existen varias implementaciones diferentes, muchas de las cuales no son tan buenas, pero puedes cambiar entre ellas sin cambiar tu código.

Sobre la base de la respuesta de zseil, puede hacer esto con una combinación de subprocesos y llamadas a la API de win32.Usé ctypes simples, porque resulta que mi Python no tiene win32api instalado.Aquí solo estoy generando sleep.exe desde MSYS como ejemplo, pero claramente puedes generar cualquier proceso que desees.Utilizo OpenProcess() para obtener un HANDLE 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!"

Twisted en Windows realizará una espera activa bajo las sábanas.Si no desea utilizar subprocesos, deberá utilizar la API 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)

Puedes usar 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 "vivo" son listas que indican qué procesos desaparecieron y cuáles siguen vivos.

Opcionalmente, puede especificar una devolución de llamada que se invoca cada vez que finaliza uno de los procesos observados:

>>> def on_terminate(proc):
...     print "%s terminated" % proc
...
>>> gone, alive = psutil.wait_procs(ls, timeout=3, callback=on_terminate)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top