2009-08-31 9 views
15

Necesito hacer lo siguiente en Python. Quiero generar un proceso (¿módulo de subproceso?) Y: ​​Python: Ejecuta un proceso y mátalo si no finaliza en una hora

  • si el proceso finaliza normalmente, continuar exactamente desde el momento en que termina;
  • si, de lo contrario, el proceso "se bloquea" y no termina dentro de (digamos) una hora, para matarlo y continuar (posiblemente dándole otra oportunidad, en un bucle).

¿Cuál es la forma más elegante de lograr esto?

Respuesta

20

El módulo subprocess será su amigo. Comience el proceso para obtener un objeto Popen, luego páselo a una función como esta. Tenga en cuenta que esto solo genera una excepción en el tiempo de espera. Si lo desea, puede capturar la excepción y llamar al método kill() en el proceso Popen. (Kill es nuevo en Python 2.6, por cierto)

import time 

def wait_timeout(proc, seconds): 
    """Wait for a process to finish, or raise exception after timeout""" 
    start = time.time() 
    end = start + seconds 
    interval = min(seconds/1000.0, .25) 

    while True: 
     result = proc.poll() 
     if result is not None: 
      return result 
     if time.time() >= end: 
      raise RuntimeError("Process timed out") 
     time.sleep(interval) 
8

Existen al menos 2 maneras de hacer esto mediante el uso de psutil, siempre y cuando se conoce el PID del proceso. Suponiendo que el proceso se crea como tal:

import subprocess 
subp = subprocess.Popen(['progname']) 

... puede obtener su tiempo de creación en un bucle ocupado como esto:

import psutil, time 

TIMEOUT = 60 * 60 # 1 hour 

p = psutil.Process(subp.pid) 
while 1: 
    if (time.time() - p.create_time) > TIMEOUT: 
     p.kill() 
     raise RuntimeError('timeout') 
    time.sleep(5) 

... o, simplemente, se puede hacer esto:

import psutil 

p = psutil.Process(subp.pid) 
try 
    p.wait(timeout=60*60) 
except psutil.TimeoutExpired: 
    p.kill() 
    raise 

Además, mientras estás en ello, que podría estar interesado en los siguientes APIs adicionales:

>>> p.status() 
'running' 
>>> p.is_running() 
True 
>>> 
2

Tuve una pregunta similar y encontré esta respuesta. Simplemente para la corrección, quiero añadir una forma más de la forma de poner fin a un proceso de reposo después de un período de tiempo determinado: La biblioteca señal de pitón https://docs.python.org/2/library/signal.html

De la documentación:

import signal, os 

def handler(signum, frame): 
    print 'Signal handler called with signal', signum 
    raise IOError("Couldn't open device!") 

# Set the signal handler and a 5-second alarm 
signal.signal(signal.SIGALRM, handler) 
signal.alarm(5) 

# This open() may hang indefinitely 
fd = os.open('/dev/ttyS0', os.O_RDWR) 

signal.alarm(0)   # Disable the alarm 

Desde que quería para desovar un nuevo proceso de todos modos, esta podría no ser la mejor solución para su problema, sin embargo.

1

Una buena manera pasiva también es mediante el uso de un subproceso. Temporizador y configuración de la función de devolución de llamada.

from threading import Timer 

# execute the command 
p = subprocess.Popen(command) 

# save the proc object - either if you make this onto class (like the example), or 'p' can be global 
self.p == p 

# config and init timer 
# kill_proc is a callback function which can also be added onto class or simply a global 
t = Timer(seconds, self.kill_proc) 

# start timer 
t.start() 

# wait for the test process to return 
rcode = p.wait() 

t.cancel() 

Si el proceso finaliza a tiempo, wait() finaliza y el código continúa aquí, cancel() detiene el temporizador. Si mientras tanto el temporizador se agota y ejecuta kill_proc en un hilo separado, wait() también continuará aquí y cancel() no hará nada. Por el valor de rcode sabrá si hemos marcado o no. Simplest kill_proc: (por supuesto, puede hacer cualquier cosa extra)

def kill_proc(self): 
    os.kill(self.p, signal.SIGTERM) 
Cuestiones relacionadas