【问题标题】:Executing a function with a timeout执行一个超时的函数
【发布时间】:2011-07-14 14:09:18
【问题描述】:

在时间限制内执行函数的惯用方式是什么?类似的,

(with-timeout 5000
 (do-somthing))

除非 do-something 返回 5000 以内,否则抛出异常或返回 nil。

编辑:在有人指出之前,

clojure (with-timeout ... macro)

但未来会继续执行,这在我的情况下不起作用。

【问题讨论】:

  • Hamza Yerlikaya 向 Clojure 提问。 3-4年后听起来仍然很奇怪。塞拉姆拉 :)

标签: clojure


【解决方案1】:

我认为您可以通过使用期货中的超时功能来合理可靠地做到这一点:

  (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来阻止future线程继续执行....

【讨论】:

    【解决方案2】:

    怎么样?

        (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】:

    这不是您可以在 JVM 上 100% 可靠地完成的事情。一段时间后停止某事的唯一方法是给它一个新线程,然后在您希望它停止时向该线程发送异常。但是他们的代码可以捕获异常,或者他们可以启动另一个您无法控制的线程,或者......

    但大多数时候,特别是如果您控制超时的代码,您可以执行我们所做的类似in clojail

    如果你想让它更漂亮,你可以定义一个宏

    (defmacro with-timeout [time & body]
      `(thunk-timeout (fn [] ~@body) ~time))
    

    【讨论】:

    【解决方案4】:

    使用 clojure 的通道工具非常容易 https://github.com/clojure/core.async

    需要各自的命名空间

    (:require [clojure.core.async :refer [>! alts!! timeout chan go]])
    

    函数等待需要超时 [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
    

    【讨论】:

    • 我不明白为什么这个(旧的)答案没有得到更多的支持。我来到这个线程寻找一些比 core.async 方式(我通常使用的方式)更容易的内置方法。但所有投票多的答案对我来说似乎更麻烦。
    【解决方案5】:

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

    【讨论】:

    • 代理仍然可以永远闲逛。这就是为什么 await-for 有返回值的原因。
    【解决方案6】:

    添加一个可能的(无宏)替代方案(当然,在接受的答案中不需要宏)

    (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))
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-16
      • 2012-04-14
      • 1970-01-01
      • 1970-01-01
      • 2014-05-10
      • 2018-08-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多