2009-11-05 18 views

Respuesta

21

Esta pregunta tiene mejores respuestas aquí: Executing a function with a timeout

de futuros para el rescate!

user=> (let [f (future (reduce * (range 1 1001)))] 
    (.get f 1 java.util.concurrent.TimeUnit/MILLISECONDS)) 
java.util.concurrent.TimeoutException (NO_SOURCE_FILE:0) 

y hacer una macro de ella:

(defmacro time-limited [ms & body] 
    `(let [f# (future [email protected])] 
    (.get f# ~ms java.util.concurrent.TimeUnit/MILLISECONDS))) 

para que pueda hacer esto:

user=> (time-limited 1 (reduce * (range 1 1001))) 
java.util.concurrent.TimeoutException (NO_SOURCE_FILE:0) 
user=> (time-limited 1 (reduce * (range 1 101))) 
93326215443944152681699238856266700490715968264381621468592963895217599993229915 
608941463976156518286253697920827223758251185210916864000000000000000000000000 
+1

Solo para ser claro - el futuro definitivamente funciona, pero ejecutará el expr proporcionado en un hilo separado. La mayoría del tiempo esto no es problema, particularmente si estás escribiendo un código referencialmente transparente, pero si estás usando (vinculando ...) definitivamente podría causar algunos problemas. – levand

+5

El futuro puede continuar existiendo después del tiempo de espera. Use 'future-cancel' después del tiempo de espera como se describe [en este hilo] (http://stackoverflow.com/a/6697469/682672). – neatonk

+0

Se puede encontrar una versión más flexible de esto que usa hilos en lugar de futuros [aquí] (http://stackoverflow.com/a/6697356/682672) – neatonk

2

No estoy seguro de que esto sea posible sin ejecutar la expresión en un hilo separado. La razón es que si el hilo está ocupado procesando la expresión, no puede inyectar código para lanzar una excepción.

Una versión con un subproceso de monitor que arroja una excepción si la expresión tarda demasiado es definitivamente posible, sin embargo, la excepción lanzada sería del subproceso del monitor, no del subproceso en el que se ejecuta la expresión. Entonces, no habría forma de detenerlo, salvo enviar una interrupción a ese hilo, que podría ignorar si no lo has codificado en la expresión.

Si es aceptable tener una versión que ejecuta la expresión en un hilo separado, hágamelo saber y puedo publicar un código de muestra. De lo contrario, su mejor apuesta sonará como escribir el bucle principal/recursión de la expresión de tal manera que compruebe cuánto tiempo ha tardado en cada iteración y arroja una excepción si se excede el límite. Lo siento si eso no es exactamente lo que necesita ...

+0

Tal vez el (with-timeout ... podría actuar como un bucle, y ser un punto recurrente, y cada vez que pulse (con-timeout comprueba el temporizador y detiene la ejecución (o lanza una excepción) si se ha estado ejecutando largo. Pero ese uso necesitaría estar * dentro * de una función, no envolviendo la función. Pero no está causando efectos secundarios a otras funciones generalmente desaconsejadas [¿Estoy pensando en todas las advertencias alrededor (vinculantes)? – nilamo

1

me encontré con este hilo recientemente, mientras que haciendo la misma pregunta. No estaba completamente satisfecho con las respuestas dadas, así que improvisé una solución alternativa. Esta solución ejecutará su código en el hilo actual y giro de un futuro para interrumpirlo después de un tiempo de espera configurado en ms.

(defn invoke-timeout [f timeout-ms] 
    (let [thr (Thread/currentThread) 
     fut (future (Thread/sleep timeout-ms) 
        (.interrupt thr))] 
    (try (f) 
     (catch InterruptedException e 
      (throw (TimeoutException. "Execution timed out!"))) 
     (finally 
      (future-cancel fut))))) 

(defmacro timeout [ms & body] `(invoke-timeout (fn [] [email protected]) ~ms)) 

Se podría utilizarlo en su código como este:

(timeout 1000 your-code) 

O

(invoke-timeout #(your-code) 1000) 

Una advertencia a tener en cuenta es que your-code no hay que coger el InterruptedException utilizado para activar la TimeoutException. Lo uso para probar y funciona bien.

Ver el Thread.interrupt()javadoc para advertencias adicionales.

Puede ver este código en uso here.