español - python async await
Python en Windows: ¿cómo esperar a múltiples procesos secundarios? (5)
Basándose en la respuesta de zseil, puede hacer esto con una combinación de llamadas API de subproceso y win32. 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!"
¿Cómo esperar múltiples procesos secundarios en Python en Windows, sin espera activa (sondeo)? Algo como esto casi funciona para mí:
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
finaliza antes de proc1
, el proceso principal seguirá esperando por 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?
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
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 se han ido y cuáles siguen vivos.
Opcionalmente, puede especificar una devolución de llamada que se invoca cada vez que finaliza uno de los procesos supervisados:
>>> def on_terminate(proc):
... print "%s terminated" % proc
...
>>> gone, alive = psutil.wait_procs(ls, timeout=3, callback=on_terminate)
Twisted en Windows realizará una espera activa debajo de las cubiertas. 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)
Twisted tiene una API de generación de procesos asíncrona 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.