是否有一种方法可以完成(Thread/sleep millis my-atom)
之类的操作,以防my-atom
在毫秒之前被更改?
还是我必须使用频道而不是手表去clojure.core.async
?
答案 0 :(得分:5)
(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
,而是将代码注册为回调函数。如果原子没有在所需的超时之前更改,则可以使用future
或core/async
来执行操作。