La forma correcta de ejecutar un código con tiempo de espera en Python

Busqué en línea y encontré algunas recetas de SO Discussing y ActiveState para ejecutar algún código con un tiempo de espera. Parece que hay algunos enfoques comunes:

  • Use el hilo que ejecuta el código y join con el tiempo de espera. Si se agotó el tiempo de espera, elimine el hilo. Esto no se admite directamente en Python (se usa la función privada _Thread__stop ) por lo que es una mala práctica
  • Use signal.SIGALRM – ¡pero este enfoque no funciona en Windows !
  • Utilice el subproceso con el tiempo de espera, pero esto es demasiado pesado . ¿Qué sucede si deseo iniciar una tarea interrumpible a menudo, no quiero un proceso de disparo para cada uno?

Entonces, ¿cuál es el camino correcto ? No pregunto por soluciones alternativas (p. Ej., Uso de Twisted y async IO), sino una forma real de resolver el problema real: tengo alguna función y quiero ejecutarlo solo con algún tiempo de espera. Si el tiempo de espera ha transcurrido, quiero recuperar el control. Y quiero que funcione en Linux y Windows.

Una solución completamente general a esto realmente, honestamente no existe. Tienes que usar la solución correcta para un dominio dado.

  • Si desea que se agoten los tiempos de espera para el código que controla completamente, debe escribirlo para cooperar. Dicho código tiene que ser capaz de dividirse en pequeños fragmentos de alguna manera, como en un sistema controlado por eventos. También puede hacer esto si se puede asegurar que nada retendrá el locking por mucho tiempo, pero manejar los lockings correctamente es bastante difícil.

  • Si desea tiempos de espera porque tiene miedo de que el código esté fuera de control (por ejemplo, si tiene miedo de que el usuario le pida a su calculadora que calcule 9**(9**9) ), debe ejecutarlo en otro proceso . Esta es la única manera fácil de aislarlo suficientemente. Ejecutarlo en tu sistema de eventos o incluso un hilo diferente no será suficiente. También es posible dividir las cosas en trozos pequeños similares a la otra solución, pero requiere un manejo muy cuidadoso y generalmente no vale la pena; en cualquier caso, eso no le permite hacer exactamente lo mismo que ejecutar el código de Python.

Lo que podría estar buscando es el módulo de multiprocesamiento . Si el subprocess es demasiado pesado, es posible que esto tampoco satisfaga sus necesidades.

 import time import multiprocessing def do_this_other_thing_that_may_take_too_long(duration): time.sleep(duration) return 'done after sleeping {0} seconds.'.format(duration) pool = multiprocessing.Pool(1) print 'starting....' res = pool.apply_async(do_this_other_thing_that_may_take_too_long, [8]) for timeout in range(1, 10): try: print '{0}: {1}'.format(duration, res.get(timeout)) except multiprocessing.TimeoutError: print '{0}: timed out'.format(duration) print 'end' 

Encontré esto con la biblioteca de eventos:

http://eventlet.net/doc/modules/timeout.html

 from eventlet.timeout import Timeout timeout = Timeout(seconds, exception) try: ... # execution here is limited by timeout finally: timeout.cancel() 

Si está relacionado con la red, puedes probar:

 import socket socket.setdefaulttimeout(number) 

Para el código “normal” de Python, que no se prolonga los tiempos prolongados en las extensiones C o las E / S en espera, puede lograr su objective configurando una función de rastreo con sys.settrace() que aborta el código en ejecución cuando se alcanza el tiempo de espera.

Si eso es suficiente o no, depende de qué tan cooperativo o malicioso sea el código que ejecuta. Si se comporta bien, una función de rastreo es suficiente.

Otra forma es usar faulthandler :

 import time import faulthandler faulthandler.enable() try: faulthandler.dump_tracebacks_later(3) time.sleep(10) finally: faulthandler.cancel_dump_tracebacks_later() 

NB: el módulo faulthandler es parte de stdlib en python3.3.

Si está ejecutando un código que espera morir después de un tiempo establecido, debe escribirlo correctamente para que no se produzcan efectos negativos en el apagado, independientemente de si se trata de un subproceso o subproceso. Un patrón de comando con deshacer sería útil aquí.

Entonces, realmente depende de lo que haga el hilo cuando lo mates. Si solo son números crujientes, a quién le importa si lo matas. Si interactúa con el sistema de archivos y lo mata, entonces tal vez debería replantearse su estrategia.

¿Qué se admite en Python cuando se trata de hilos? Hilos y uniones de daemon. ¿Por qué Python deja que el hilo principal salga si te has unido a un demonio mientras aún está activo? Porque se entiende que alguien que use subprocesos de daemon (con suerte) escribirá el código de una manera que no importará cuando ese subproceso muera. En este contexto, es perfectamente aceptable dar un tiempo de espera a una unión y luego dejar el dado principal y, por lo tanto, llevar cualquier subproceso de daemon con ella.

Lo he resuelto de esa manera: para mí está muy bien (en ventanas y no demasiado pesado) Espero que haya sido útil para alguien)

 import threading import time class LongFunctionInside(object): lock_state = threading.Lock() working = False def long_function(self, timeout): self.working = True timeout_work = threading.Thread(name="thread_name", target=self.work_time, args=(timeout,)) timeout_work.setDaemon(True) timeout_work.start() while True: # endless/long work time.sleep(0.1) # in this rate the CPU is almost not used if not self.working: # if state is working == true still working break self.set_state(True) def work_time(self, sleep_time): # thread function that just sleeping specified time, # in wake up it asking if function still working if it does set the secured variable work to false time.sleep(sleep_time) if self.working: self.set_state(False) def set_state(self, state): # secured state change while True: self.lock_state.acquire() try: self.working = state break finally: self.lock_state.release() lw = LongFunctionInside() lw.long_function(10) 

La idea principal es crear un hilo que se duerma en paralelo al “trabajo prolongado” y al despertar (después del tiempo de espera) cambiar el estado de la variable asegurada, la función larga que verifica la variable asegurada durante su trabajo. Soy bastante nuevo en la progtwigción de Python, por lo que si esa solución tiene errores fundamentales, como recursos, tiempo, problemas de puntos muertos, por favor, responda).

resolviendo con el ‘con’ construir y fusionando solución de –

  • Función de tiempo de espera si tarda demasiado tiempo en terminar
  • Este hilo que funciona mejor.

     import threading, time class Exception_TIMEOUT(Exception): pass class linwintimeout: def __init__(self, f, seconds=1.0, error_message='Timeout'): self.seconds = seconds self.thread = threading.Thread(target=f) self.thread.daemon = True self.error_message = error_message def handle_timeout(self): raise Exception_TIMEOUT(self.error_message) def __enter__(self): try: self.thread.start() self.thread.join(self.seconds) except Exception, te: raise te def __exit__(self, type, value, traceback): if self.thread.is_alive(): return self.handle_timeout() def function(): while True: print "keep printing ...", time.sleep(1) try: with linwintimeout(function, seconds=5.0, error_message='exceeded timeout of %s seconds' % 5.0): pass except Exception_TIMEOUT, e: print " attention !! execeeded timeout, giving up ... %s " % e