2013-03-15 73 views
3

我今天在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!@aderef和调用swap!然后我可以换出一个值,该值是不42.

之间是相同的原子

我该如何编写一个正确返回两个值(换出和换入)的函数?

我不关心原子变化的各种值:我想知道的只是换出的值。

这可以使用保证不死锁的代码来编写,如果是这样的话,为什么?

回答

2

原子是不协调的,所以任何试图在交换函数之外执行此操作的尝试似乎都可能失败。你可以写一个函数,而不是交换!它构造一个函数,在应用实函数之前保存现有值,然后将此构造的函数传递给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] 

这个例子打印它,它也将是有意义的他们推到存储在另一个原子

12

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

如果你想返回值,斯图尔特回答是正确的,但如果你只是打算做一堆的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))) 
+0

的*调用println *只是举一个例子,但是......现在我知道*添加手表*我可以找到伟大的用途为了那个原因 ; ) – 2013-03-16 15:12:17

1

你可以使用像宏:

(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 {}} 
0

你可以依靠的承诺STO重新运行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]))