我今天谈到这一点上IRC的#clojure渠道,但想走得更在这里详细。 基本上,为了更好地理解原子, swap!
, deref
和Clojure的并发作为一个整体,我想尝试写一个函数,它不仅返回被交换,在使用值swap!
,而且这是换出的值。
(def foo (atom 42))
.
.
.
((fn [a]
(do
(println "swapped out: " @a)
(println "swapped in: "(swap! a rand-int)))) foo)
可以打印:
swapped out: 42
swapped in: 14
但是,如果另一个线程做swap!
之间同一原子@a
deref
和电话swap!
然后我可以换出一个值,该值是不42。
我怎么能写一个函数,还给正常两个值(换出和换入)?
我不关心的是,原子不会更改为不同的值:所有我想知道的是什么换出的值。
可以这样使用保证不会僵局,如果是的话,为什么代码写?
Clojure的swap!
只是一个纺纱比较和设置。 您可以定义返回任何你喜欢的替代版本:
(defn alternate-swap [atom f & args]
(loop []
(let [old @atom
new (apply f old args)]
(if (compare-and-set! atom old new)
[old new] ; return value
(recur)))))
原子是未协调,以便它很可能是任何试图做到这一点的交换功能外它的自我很可能会失败。 你可以写你打电话,而不是交换的功能! 该方法构造应用的真正功能之前保存现有的值的函数,然后通过这个构造函数来swap!
。
user> (def foo (atom []))
#'user/foo
user> (defn save-n-swap! [a f & args]
(swap! a (fn [old-val]
(let [new-val (apply f (cons old-val args))]
(println "swapped out: " old-val "\n" "swapped in: " new-val)
new-val))))
#'user/save-n-swap!
user> (save-n-swap! foo conj 4)
swapped out: []
swapped in: [4]
[4]
user> (save-n-swap! foo conj 4)
swapped out: [4]
swapped in: [4 4]
[4 4]
这个例子打印它,它也将是有意义的他们推到存储在另一个原子更新日志
如果你想返回值,斯图尔特回答是正确的,但如果你只是打算做一堆的println了解原子/裁判是如何工作的,我会推荐给手表添加到原子/ REF HTTP:// clojuredocs.org/clojure_core/1.2.0/clojure.core/add-watch
(add-watch your-atom :debug (fn [_ _ old new] (println "out" old "new" new)))
你可以使用像宏:
(defmacro swap!-> [atom & args]
`(let [old-val# (atom nil)
new-val# (swap! ~atom #(do
(swap! old-val# (constantly %))
(-> % ~args)))]
{:old @old-val# :new new-val#}))
(def data (atom {}))
(swap!-> data assoc :a 3001)
=> {:new {:a 3001} :old {}}
你可以依靠的承诺来存储内部的电流值swap!
操作。 然后你在一个载体回报新老值,如下所示:
(defn- swap-and-return-old-value!
[^clojure.lang.IAtom atom f & args]
(let [old-value-promise (promise)
new-value (swap! atom
(fn [old-value]
(deliver old-value-promise old-value)
(apply f old-value args)))]
[new-value @old-value-promise]))