2010-08-03 7 views
5

Quieropython: enfoque simple para matar niños o reportar su éxito?

  1. comandos shell llamada (por ejemplo 'sueño' a continuación) en paralelo,
  2. informe sobre sus inicios y terminaciones individuales y
  3. ser capaz de matarlos con 'kill -9 parent_process_pid '.

Ya hay mucho escrito sobre este tipo de cosas, pero siento que aún no he encontrado la elegante solución pitonica que estoy buscando. También estoy tratando de mantener las cosas relativamente legibles (y cortas) para alguien que no esté familiarizado con Python.

Mi enfoque hasta ahora (ver código de abajo) ha sido:

  1. puesto subprocess.call (mandato_unix) en una función contenedora que informa del inicio y finalización del comando.
  2. llama a la función de envoltura con multiproceso.Proceso.
  3. rastrear los pids apropiados, almacenarlos globalmente, y matarlos en el signal_handler.

Estaba tratando de evitar una solución que periódicamente encuesta los procesos, pero no estoy seguro de por qué.

¿Hay un mejor enfoque?

import subprocess,multiprocessing,signal 
import sys,os,time 

def sigterm_handler(signal, frame): 
     print 'You killed me!' 
     for p in pids: 
       os.kill(p,9) 
     sys.exit(0) 

def sigint_handler(signal, frame): 
     print 'You pressed Ctrl+C!' 
     sys.exit(0) 

signal.signal(signal.SIGINT, sigint_handler) 
signal.signal(signal.SIGTERM, sigterm_handler) 

def f_wrapper(d): 
     print str(d) + " start" 
     p=subprocess.call(["sleep","100"]) 
     pids.append(p.pid) 
     print str(d) + " done" 

print "Starting to run things." 

pids=[] 

for i in range(5): 
     p=multiprocessing.Process(target=f_wrapper,args=(i,)) 
     p.daemon=True 
     p.start() 

print "Got things running ..." 

while pids: 
     print "Still working ..." 
     time.sleep(1) 
+8

+1 por un título llamativo. –

+1

No sabía que Python era capaz de matar niños ... –

+0

@ Zonda333: Oh, claro. 'desde __future__ import murder '. –

Respuesta

4

Una vez subprocess.call retornos, el sub-proceso se lleva a cabo - y el valor de retorno call 's es la de returncode sub-proceso. Entonces, acumular esos códigos de retorno en la lista pids (que btw no está sincronizado entre el multiproceso que lo agrega y el proceso "principal") y enviarles 9 señala "como si" fueran identificadores de proceso en lugar de códigos de retorno, definitivamente incorrecto.

Otra cosa con la pregunta que está definitivamente mal es la especificación:

ser capaz de matarlos con 'kill -9 parent_process_pid'.

ya que el significa que el proceso padre no puede posiblemente interceptar la señal (que es el objetivo de especificar explícitamente) - Me imagino que el es, por lo tanto falsa aquí.

Debería estar usando threading en lugar de multiprocessing (cada hilo o proceso de "niñera" no hace más que esperar su subproceso, entonces ¿por qué desperdiciar procesos en una tarea tan liviana? -); también debe llamar al suprocess.Process en el hilo principal (para iniciar el subproceso y poder obtener su .pid para ponerlo en la lista) y pasar el objeto de proceso resultante al hilo de canguro que lo espera (y cuando se hace informes y lo elimina de la lista). La lista de identificadores de subprocesos debe estar protegida por un candado, ya que el hilo principal y varios hilos de canguro pueden acceder a él, y un conjunto probablemente sería una mejor opción que una lista (extracciones más rápidas) ya que no te importa ordenar ni sobre evitar duplicados.

Así, más o menos (sin pruebas, por lo que podría haber errores ;-) que cambiaría su código de s/algo como:

import subprocess, threading, signal 
import sys, time 

pobs = set() 
pobslock = threading.Lock() 
def numpobs(): 
    with pobslock: 
     return len(pobs) 

def sigterm_handler(signal, frame): 
    print 'You killed me!' 
    with pobslock: 
     for p in pobs: p.kill() 
    sys.exit(0) 

def sigint_handler(signal, frame): 
    print 'You pressed Ctrl+C!' 
    sys.exit(0) 

signal.signal(signal.SIGINT, sigint_handler) 
signal.signal(signal.SIGTERM, sigterm_handler) 

def f_wrapper(d, p): 
    print d, 'start', p.pid 
    rc = p.wait() 
    with pobslock: 
     pobs.remove(p) 
    print d, 'done, rc =', rc 

print "Starting to run things." 

for i in range(5): 
    p = subprocess.Popen(['sleep', '100']) 
    with pobslock: 
     pobs.add(p) 
    t = threading.Thread(target=f_wrapper, args=(i, p)) 
    t.daemon=True 
    t.start() 

print "Got things running ..." 

while numpobs(): 
    print "Still working ..." 
    time.sleep(1) 
+0

Gracias, eso es muy útil. ¡Volveré a publicar una vez que lo limpie! – mathtick

+0

@mathtick, de nada! He editado mi A para mostrar cómo, más o menos, vería el código mejor estructurado. –

+0

Esto, http://speculation.org/garrick/kill-9.html, fue una buena lección para mí. Creo que recogí el hábito de "-9" cuando toqué por primera vez una caja de linux. – mathtick

2

Este código (código de abajo) parece funcionar para mí, matando desde "arriba" o ctrl-c desde la línea de comando. El único cambio real de las sugerencias de Alex fue reemplazar el subproceso. Procesar con un subproceso. Llamada a puerta cerrada (no creo que el subproceso exista).

El código aquí también podría mejorarse de alguna manera bloqueando la salida estándar para que no haya posibilidad de superposición de impresión entre los procesos.

import subprocess, threading, signal 
import sys, time 

pobs = set()       # set to hold the active-process objects 
pobslock = threading.Lock()  # a Lock object to make sure only one at a time can modify pobs 

def numpobs(): 
     with pobslock: 
       return len(pobs) 

# signal handlers 
def sigterm_handler(signal, frame): 
     print 'You killed me! I will take care of the children.' 
     with pobslock: 
       for p in pobs: p.kill() 
     sys.exit(0) 

def sigint_handler(signal, frame): 
     print 'You pressed Ctrl+C! The children will be dealt with automatically.' 
     sys.exit(0) 

signal.signal(signal.SIGINT, sigint_handler) 
signal.signal(signal.SIGTERM, sigterm_handler) 


# a function to watch processes 
def p_watch(d, p): 
     print d, 'start', p.pid 
     rc = p.wait() 
     with pobslock: 
       pobs.remove(p) 
     print d, 'done, rc =', rc 


# the main code 
print "Starting to run things ..." 
for i in range(5): 
     p = subprocess.Popen(['sleep', '4']) 
     with pobslock: 
       pobs.add(p) 
     # create and start a "daemon" to watch and report the process p. 
     t = threading.Thread(target=p_watch, args=(i, p)) 
     t.daemon=True 
     t.start() 

print "Got things running ..." 
while numpobs(): 
     print "Still working ..." 
     time.sleep(1) 
+0

tiene razón en que no hay subproceso. Proceda, disculpe el experto; edite mi A ahora para solucionarlo. –

Cuestiones relacionadas