替代版本的交换!还返回换出的值

Ced*_*tin 3 concurrency multithreading atomic clojure

我今天在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)
Run Code Online (Sandbox Code Playgroud)

可以打印:

swapped out:  42
swapped in:   14
Run Code Online (Sandbox Code Playgroud)

但是,如果另一个线程swap!@a deref和调用之间执行相同的原子,swap!那么我可能会交换一个不是42的值.

如何编写一个能正确回放两个值(交换掉和交换掉)的函数?

我并不关心原子确实改变的各种值:我想知道的是所交换的值是多少.

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

Stu*_*rra 13

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)))))
Run Code Online (Sandbox Code Playgroud)


Art*_*ldt 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] 
Run Code Online (Sandbox Code Playgroud)

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