clojure(with-timeout ... macro)

时间:2009-11-05 21:17:26

标签: clojure

我正在寻找一个宏,如果表达式需要超过X秒才能完成异常。

3 个答案:

答案 0 :(得分:22)

这个问题在这里有更好的答案: Executing a function with a timeout

期待救援!

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

并制作一个宏:

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

所以你可以这样做:

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 :(得分:2)

如果不在单独的线程中运行表达式,我不确定这是否可行。原因是,如果线程忙于处理表达式,则无法注入代码以引发异常。

具有监视器线程的版本如果表达式花费太长时会抛出异常,但是,抛出的异常将来自监视器线程,而不是运行表达式的线程。然后,没有办法阻止它向该线程发送一个中断,如果你没有在表达式中对它进行编码,它可能会忽略它。

如果有一个版本在一个单独的线程中运行表达式是可以接受的,请告诉我,我可以发布一些示例代码。否则,你最好的选择听起来像是以这样的方式编写表达式的主循环/递归,它会检查它在每次迭代中花了多长时间,如果它超出了边界则抛出异常。对不起,如果那不是你需要的......

答案 2 :(得分:1)

我在问同样的问题时最近遇到了这个帖子。我对给出的答案并不完全满意,所以我拼凑了一个替代解决方案。此解决方案将在当前线程中运行您的代码并在未来旋转以在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 [] ~@body) ~ms))

您可以在代码中使用它:

(timeout 1000 your-code)

OR

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

需要记住的一点需要注意:your-code一定不能捕获用于触发TimeoutException的InterruptedException。我用它进行测试,效果很好。

有关其他警告,请参阅Thread.interrupt() javadoc

您可以看到此代码正在使用here