2010-11-11 12 views
16

Me gustaría ejecutar repetidamente un subproceso lo más rápido posible. Sin embargo, a veces el proceso llevará demasiado tiempo, así que quiero matarlo. utilizo signal.signal (...), como a continuación:¿Matar o terminar el subproceso cuando se agota el tiempo de espera?

ppid=pipeexe.pid 
signal.signal(signal.SIGALRM, stop_handler) 

signal.alarm(1) 
..... 
def stop_handler(signal, frame): 
    print 'Stop test'+testdir+'for time out' 
    if(pipeexe.poll()==None and hasattr(signal, "SIGKILL")): 
     os.kill(ppid, signal.SIGKILL) 
     return False 

pero en algún momento este código a tratar de detener la siguiente ronda de la ejecución. Prueba de detención/home/lu/workspace/152/treefit/test2 para tiempo de espera /bin/sh:/home/lu/workspace/153/squib_driver: no encontrado --- esta es la siguiente ejecución; el programa lo detiene incorrectamente.

¿Alguien sabe cómo solucionar esto? Quiero detenerme a tiempo para no ejecutar 1 segundo el tiempo. Sueño (n) a menudo espera n segundos. No quiero que lo quiera. Puede ejecutar menos de 1 segundo

+0

¿Entonces, esencialmente si el subproceso se ejecuta durante más de 1 segundo, desea matarlo y comenzar el siguiente? ¿Es esto correcto? –

+0

¿Cómo se crea su subproceso? porque parece que la expresión __ppid = pipeexe.pid__ está obteniendo el siguiente subproceso que se ejecutará !!! – mouad

+0

Entonces, esencialmente si el subproceso se ejecuta durante más de 1 segundo, ¿quiere matarlo y comenzar el siguiente? ¿Es esto correcto? sí, eso es correcto – user504909

Respuesta

35

se podría hacer algo como esto:

import subprocess as sub 
import threading 

class RunCmd(threading.Thread): 
    def __init__(self, cmd, timeout): 
     threading.Thread.__init__(self) 
     self.cmd = cmd 
     self.timeout = timeout 

    def run(self): 
     self.p = sub.Popen(self.cmd) 
     self.p.wait() 

    def Run(self): 
     self.start() 
     self.join(self.timeout) 

     if self.is_alive(): 
      self.p.terminate()  #use self.p.kill() if process needs a kill -9 
      self.join() 

RunCmd(["./someProg", "arg1"], 60).Run() 

La idea es que se crea un hilo que corre el comando y para acabar con él si el tiempo de espera supera un cierto valor adecuado, en este caso 60 segundos.

+1

+1. Este método es el que uso en mi sistema de compilación para probar ejecutables. – Macke

+5

También +1. Este parece ser uno de los métodos más limpios que he visto hasta ahora. Con modificaciones menores funciona también en Python <2.6 en Linux usando os.kill (self.p.pid, signal.SIGKILL) (o SIGTERM seguido de SIGKILL). También puede reemplazar self.p.wait() con self.out, self.err = self.p.communicate() para evitar el bloqueo del subproceso si llena los tubos stdout/stderr – FredL

+0

Pegué este código y mi comando parece que comenzar correctamente, sin embargo, necesito ejecutar un hilo a la vez. Comience un proceso, déjelo terminar de forma natural o elimínelo si tarda demasiado, luego comience nuevamente. –

0

Supongo que este es un problema de sincronización común en la programación orientada a eventos con subprocesos y procesos.

Si siempre debe tener un solo subproceso ejecutándose, asegúrese de que el subproceso actual se elimine antes de ejecutar el siguiente. De lo contrario, el manejador de señal puede obtener una referencia al último subproceso ejecutado e ignorar el anterior.

Supongamos que se está ejecutando el subproceso A. Antes de manejar la señal de alarma, se inicia el subproceso B. Justo después de eso, su manejador de señal de alarma intenta matar un subproceso. Como el PID actual (o el objeto de la tubería del subproceso actual) se configuró en B al iniciar el subproceso, B muere y A se sigue ejecutando.

¿Mi conjetura es correcta?

Para hacer que su código sea más fácil de entender, incluiría la parte que crea un nuevo subproceso justo después de la parte que elimina el subproceso actual. Eso dejaría en claro que solo hay un subproceso ejecutándose en cualquier momento. El manejador de señal podría hacer tanto el subproceso matando y lanzando, como si fuera el bloque de iteración que se ejecuta en un bucle, en este caso controlado por eventos con la señal de alarma cada 1 segundo.

2

Aquí hay algo que escribí como un perro guardián para la ejecución del subproceso. Yo uso ahora mucho, pero no estoy tan experimentado así que tal vez hay algunos defectos en él:

import subprocess 
import time 

def subprocess_execute(command, time_out=60): 
    """executing the command with a watchdog""" 

    # launching the command 
    c = subprocess.Popen(command) 

    # now waiting for the command to complete 
    t = 0 
    while t < time_out and c.poll() is None: 
     time.sleep(1) # (comment 1) 
     t += 1 

    # there are two possibilities for the while to have stopped: 
    if c.poll() is None: 
     # in the case the process did not complete, we kill it 
     c.terminate() 
     # and fill the return code with some error value 
     returncode = -1 # (comment 2) 

    else:     
     # in the case the process completed normally 
     returncode = c.poll() 

    return returncode 

de uso:

return = subprocess_execute(['java', '-jar', 'some.jar']) 

Comentarios:

  1. aquí, el el tiempo de espera del perro guardián es en segundos; pero es fácil cambiar a lo que sea necesario cambiando el valor time.sleep(). El time_out deberá documentarse en consecuencia;
  2. según lo que se necesite, aquí quizás sea más adecuado plantear algunas excepciones.

Documentación: Me ha costado un poco con la documentación del módulo de subprocess entender que subprocess.Popen no bloquea; el proceso se ejecuta en paralelo (tal vez no use la palabra correcta aquí, pero creo que es comprensible).

Pero como lo que escribí es lineal en su ejecución, realmente tengo que esperar a que termine el comando, con un tiempo de espera para evitar errores en el comando para pausar la ejecución nocturna del script.

0

Esto es lo que yo uso:

class KillerThread(threading.Thread): 
    def __init__(self, pid, timeout, event): 
    threading.Thread.__init__(self) 
    self.pid = pid 
    self.timeout = timeout 
    self.event = event 
    self.setDaemon(True) 
    def run(self): 
    self.event.wait(self.timeout) 
    if not self.event.isSet() : 
     try: 
     os.kill(self.pid, signal.SIGKILL) 
     except OSError, e: 
     #This is raised if the process has already completed 
     pass  

def runTimed(dt, dir, args, kwargs): 
    event = threading.Event() 
    cwd = os.getcwd() 
    os.chdir(dir) 
    proc = subprocess.Popen(args, **kwargs) 
    os.chdir(cwd) 
    killer = KillerThread(proc.pid, dt, event) 
    killer.start() 

    (stdout, stderr) = proc.communicate() 
    event.set()  

    return (stdout,stderr, proc.returncode) 
0

un poco más complejo, que añade un answer to solve a similar problem: Captura de la salida estándar, la alimentación de la entrada estándar, y ser capaz de terminar después de algún tiempo de inactividad y/o después de algún tiempo de ejecución global.

Cuestiones relacionadas