Estoy buscando una macro que lanzará una excepción si una expresión tarda más de X segundos en completarse.clojure (with-timeout ... macro)
Respuesta
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
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 ...
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
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.
- 1. Clojure problema macro
- 2. clojure cuando macro
- 3. Definición Clojure sintaxis macro
- 4. Aplicar macro recurrente en Clojure
- 5. Tratar macro Clojure como una función
- 6. Macro de subprocesamiento generalizado en Clojure
- 7. argumentos de la macro en clojure
- 8. Problemas con clojure Cotización en paren `(...) macro
- 9. ¿Cómo evitar el macro anafórico en Clojure?
- 10. ¿Cómo puedo mapear una macro en Clojure?
- 11. devolver varios valores de una clojure macro
- 12. Clojure macro as function/'Partial' for Macros?
- 13. Clojure: ¿el macro de envío es especial?
- 14. En clojure, ¿cómo puedo evaluar los argumentos a una macro desde otra macro?
- 15. ¿Cómo podría escribir una macro "defn" en Clojure?
- 16. define un sinónimo para una macro de Clojure
- 17. Llamadas a un método dinámico en una macro Clojure?
- 18. Clojure macro que conservará la orden de asignación asociativa
- 19. macro macro dependiente
- 20. macro -> con funciones anónimas
- 21. ¿Puedo hacer una macro de clojure que me permita obtener una lista de todas las funciones creadas por la macro?
- 22. soporte macro en F #
- 23. Clojure defmacro pierde metadatos
- 24. Evaluación diferida en Clojure
- 25. ¿Cómo implementar un sistema macro Lisp?
- 26. Preprocesador C Macro que define Macro
- 27. definición constante en Clojure
- 28. Clojure coalesce function
- 29. with-meta vs^{} - Clojure
- 30. Evaluación Clojure de macros
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
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
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