Question

Comment attendre plusieurs processus enfants dans Python sous Windows, sans attente active (polling)? Quelque chose comme ça presque fonctionne pour moi:

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

Le problème est que lorsque proc2 se termine avant le proc1 , le processus parent attendra toujours proc1 . Sous Unix, on utiliserait waitpid (0) dans une boucle pour obtenir les codes de retour des processus enfants à la fin - comment obtenir quelque chose comme cela en Python sous Windows?

Était-ce utile?

La solution

Cela peut sembler exagéré, mais le voici:

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

Autres conseils

Twisted possède une API de génération de processus asynchrone qui fonctionne sous Windows. Il y a en fait plusieurs implémentations différentes, dont beaucoup ne sont pas si grandes, mais vous pouvez basculer entre elles sans changer votre code.

En vous basant sur la réponse de zseil, vous pouvez le faire avec un mélange d’appels de sous-processus et d’API Win32. J'ai utilisé des types directs, parce que mon python ne dispose pas de win32api. À titre d'exemple, je suis en train de générer sleep.exe à partir de MSYS, mais il est clair que vous pouvez générer n'importe quel processus. J'utilise OpenProcess () pour obtenir un HANDLE à partir du PID du processus, puis WaitForMultipleObjects pour attendre la fin du processus.

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 sur Windows effectuera une attente active sous les couvertures. Si vous ne souhaitez pas utiliser de threads, vous devrez utiliser l'API win32 pour éviter les interrogations. Quelque chose comme ça:

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)

Vous pouvez utiliser 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)

'gone' et 'alive' sont des listes indiquant quels processus sont partis et lesquels sont encore en vie.

Vous pouvez éventuellement spécifier un rappel qui est appelé chaque fois qu'un des processus surveillés se termine:

>>> def on_terminate(proc):
...     print "%s terminated" % proc
...
>>> gone, alive = psutil.wait_procs(ls, timeout=3, callback=on_terminate)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top