等待原子变化超时

时间:2018-06-20 23:15:59

标签: clojure

是否有一种方法可以完成(Thread/sleep millis my-atom)之类的操作,以防my-atom在毫秒之前被更改?

还是我必须使用频道而不是手表去clojure.core.async

3 个答案:

答案 0 :(得分:5)

您可以使用promise使用deref并设置超时时间:

(def p (promise))
(future ;; some other thread starts working with the promise
  (Thread/sleep 500)
  (deliver p :finished-early))
(deref p 1000 :timed-out) ;; => :finished-early

如果睡眠时间长于1000,则deref将返回:timed-out

更新:我看到您的问题现在更具体地是关于原子的。在那种情况下,您仍然可以通过在原子上添加 watch 并使用诺言,如果值发生更改,则兑现诺言:

(def p (promise))
(def a (atom {}))
(add-watch a :watch-changed
           (fn [_ _ old new]
             (when-not (= old new) (deliver p :changed))))
(future
  (Thread/sleep 1001)
  (swap! a assoc :foo 1))
(deref p 1000 :timed-out) ;; => :changed

或者以可重用函数形式,r可以是任何IRef类型:

(defn await-change [r timeout-ms]
  (let [p (promise)]
    (try
      (add-watch r :await-change ;; keyword must be unique per ref!
                 (fn [_ _ old new]
                   (when-not (= old new) (deliver p :changed))))
      (deref p timeout-ms :timed-out)
      (finally
        (remove-watch r :await-change)))))

(def a (atom {}))
(future
  (Thread/sleep 500)
  (swap! a assoc :foo 1))
(await-change a 1000) ;; => :changed

答案 1 :(得分:0)

这是一个通用的解决方案,它会重复睡眠一小段时间,然后在睡眠之间调用break-f,以检查它是否应该唤醒并返回。

如果是原子,它可以例如像(interruptible-sleep 100 #(not @my-atom))

一样使用

投票并不是一个很好的概念,但有时就足够了:

(defn interruptible-sleep
  ([millis break-f] (interruptible-sleep millis break-f 100))
  ([millis break-f polling-millis]
   (if (> millis 0)
     (let [end (+ (System/currentTimeMillis) millis)]
       (loop []
         (let [now (System/currentTimeMillis)
               remaining-millis (- end now)]
           (if (and (> remaining-millis 0) (not (break-f)))
             (do
               (if (>= remaining-millis 5)
                 (Thread/sleep (min polling-millis remaining-millis)))
               (recur))
             (- remaining-millis))))))))

答案 2 :(得分:0)

虽然使用promise的解决方案很好,但是一个Promise只能设置一次,此后是不可变的。因此,这并不是问题中atom的直接替代。

更好的解决方案是坚持使用atom,然后坚持use the add-watch function来指定一个观察者,只要原子的值发生变化,该观察者就会得到通知:

(def a (atom {}))

(add-watch a :watcher
  (fn [key atom old-state new-state]
    (prn "-- Atom Changed --")
    (prn "key" key)
    (prn "atom" atom)
    (prn "old-state" old-state)
    (prn "new-state" new-state)))

(reset! a {:foo "bar"})

有结果:

"-- Atom Changed --"
"key"        :watcher
"atom"       #<Atom@4b020acf: {:foo "bar"}>
"old-state"  {}
"new-state"  {:foo "bar"} 

{:foo "bar"}

您的代码将需要重新架构,因此无需将超时调用deref,而是将代码注册为回调函数。如果原子没有在所需的超时之前更改,则可以使用futurecore/async来执行操作。