4

我今天在 IRC 的#clojure频道上谈到了这一点,但我想在这里更详细地介绍一下。基本上,为了更好地理解原子、swap!derefClojure 并发作为一个整体,我想尝试编写一个函数,它不仅返回使用 换入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!之间执行相同的原子,那么我可能会换出一个不是 42 的值。@a derefswap!

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

我不关心原子确实更改为的各种值:我只想知道换出的值是什么。

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

4

6 回答 6

16

Clojureswap!只是一个旋转的比较和设置。您可以定义返回任何您喜欢的替代版本:

(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)))))
于 2013-03-15T21:07:49.750 回答
3

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

此示例打印它,将它们推送到存储在另一个原子中的更改日志也是有意义的

于 2013-03-15T20:43:01.527 回答
3

如果你想要返回值,Stuart 的答案是正确的,但如果你只是要做一堆 println 来了解 atom/refs 是如何工作的,我建议在 atom/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)))
于 2013-03-16T07:53:32.430 回答
2

您可以使用如下宏:

(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 {}}
于 2013-03-16T08:11:12.020 回答
1

参考swap-vals!从 1.9 开始可用:https ://clojuredocs.org/clojure.core/swap-vals%21

于 2021-01-24T15:17:16.480 回答
0

您可以依靠承诺将当前值存储在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]))
于 2017-10-13T21:51:49.003 回答