执行超时功能

时间:2011-07-14 14:09:19

标签: clojure

在一个时间限制内执行函数的惯用方法是什么?像,

(with-timeout 5000
 (do-somthing))

除非do-something在5000内返回抛出异常或返回nil。

编辑:在有人指出它之前,

clojure (with-timeout ... macro)

但随之而来的是未来一直在执行,这在我的情况下是行不通的。

6 个答案:

答案 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)

您可以使用agent,然后使用await-for

答案 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))

需要两个线程,但只是一个想法。