在一个时间限制内执行函数的惯用方法是什么?像,
(with-timeout 5000
(do-somthing))
除非do-something在5000内返回抛出异常或返回nil。
编辑:在有人指出它之前,
clojure (with-timeout ... macro)
但随之而来的是未来一直在执行,这在我的情况下是行不通的。
答案 0 :(得分:15)
我认为你可以通过在期货中使用超时功能来合理可靠地做到这一点:
(defmacro with-timeout [millis & body]
`(let [future# (future ~@body)]
(try
(.get future# ~millis java.util.concurrent.TimeUnit/MILLISECONDS)
(catch java.util.concurrent.TimeoutException x#
(do
(future-cancel future#)
nil)))))
一些实验证实你需要做一个future-cancel来阻止未来的线程继续执行....
答案 1 :(得分:13)
这不是你可以在JVM上100%可靠地完成的事情。一段时间后停止某事的唯一方法是给它一个新线程,然后在你希望它停止时发送该线程异常。但是他们的代码可以捕获异常,或者他们可以启动另一个你无法控制的线程,或者......
但大部分时间,特别是如果您控制正在超时的代码,您可以执行类似in clojail的操作:
如果你想做得更漂亮,你可以定义一个像
这样的宏(defmacro with-timeout [time & body]
`(thunk-timeout (fn [] ~@body) ~time))
答案 2 :(得分:7)
怎么样?
(defn timeout [timeout-ms callback]
(let [fut (future (callback))
ret (deref fut timeout-ms ::timed-out)]
(when (= ret ::timed-out)
(future-cancel fut))
ret))
(timeout 100 #(Thread/sleep 1000))
;=> :user/timed-out
答案 3 :(得分:2)
使用clojure的频道设施是一件轻而易举的事 https://github.com/clojure/core.async
需要相应的命名空间
(:require [clojure.core.async :refer [>! alts!! timeout chan go]])
函数wait需要超时[ms],函数[f]和可选参数[args]
(defn wait [ms f & args]
(let [c (chan)]
(go (>! c (apply f args)))
(first (alts!! [c (timeout ms)]))))
第三行将对f的调用弹出到另一个线程。第四行消耗函数调用的结果或(如果更快)超时。
考虑以下示例调用
(wait 1000 (fn [] (do (Thread/sleep 100) 2)))
=> 2
但
(wait 50 (fn [] (do (Thread/sleep 100) 2)))
=> nil
答案 4 :(得分:0)
答案 5 :(得分:0)
为混音添加一个可能的(无宏)替代方案(当然,在接受的答案中不需要宏)
(defn with-timeout [f ms]
(let [p (promise)
h (future
(deliver p (f)))
t (future
(Thread/sleep ms)
(future-cancel h)
(deliver p nil))]
@p))
需要两个线程,但只是一个想法。