【发布时间】:2013-11-19 05:45:57
【问题描述】:
使用单个 atom 和 hashmap 与多个 ref 表示状态的权衡是什么?
例如:
(def start (atom {:location "Chicago" :employer "John"}))
对
(def location (ref "Chicago"))
(def employer (ref "John"))
非常感谢
【问题讨论】:
标签: concurrency clojure stm
使用单个 atom 和 hashmap 与多个 ref 表示状态的权衡是什么?
例如:
(def start (atom {:location "Chicago" :employer "John"}))
对
(def location (ref "Chicago"))
(def employer (ref "John"))
非常感谢
【问题讨论】:
标签: concurrency clojure stm
单原子版本更好,权衡更少。鉴于您不想更改雇主和不协调的位置,您的胜利是您不必创建dosync 块来更改位置或雇主或两者。使用原子,您可以简单地(swap! start assoc :location "baz")。
使用多个 refs 的一个重大权衡是,所有与 refs 的交易都将并行尝试,第一个准备好的交易将获胜,其他交易将重新启动。虽然对于 atom 也是如此,但为所有条目拥有更多的 ref 需要在幕后进行更多的监控、分组(对于 dosync 块)等。为了减少重新启动,将信息分组到哈希图中是有意义的。根据是否需要协调更改,将其放在 ref 或 atom 中。
【讨论】:
多个 Ref 允许更多的并发性,因为所有对 Atom 的写入都是线性化的。当没有冲突的写入时,STM 允许提交许多并行事务/ensures(此外,它还提供了commute,它允许进行某些通常会导致冲突的写入不这样做)。
此外,STM 与代理合作——当且仅当事务提交时,才会执行从事务中发送到代理的操作。这允许人们从交易内部安全地产生副作用。 Atoms 没有提供类似的功能。
权衡是 STM 的开销大于 Atom 的开销,而且有可能发生某些异常(写入偏差,请参阅 the Wikipedia page on snapshot isolation)。此外,在获取整个系统的快照时遇到严重问题时,可以使用 STM 实现出色的并发性;在这方面,请参阅 Christophe Grand 出色的 blog post 和他的 megaref 库。
在许多情况下,人们发现只需将所有状态存储在单个 Atom 中就足够了,这绝对是一种更简单、更轻量级的方法。
【讨论】:
Additionally, the STM cooperates with Agents -- actions sent to Agents from within a transaction will be performed if and only if the transaction commits. - 交换原子时不会执行操作吗?这看起来像是无论成功与否都会执行的操作。
swap! 的函数中send,那么是的,send 将在每次重试时重新执行。尝试例如(def a (atom 0)) (def ag (agent nil)) (do (future (swap! a (fn [x] (Thread/sleep 5000) (send ag println :foo) (inc x)))) (Thread/sleep 1000) (swap! a inc)):这里:foo会被打印两次;如果你使用 Ref 而不是 Atom,它只会被打印一次。
我认为您不应该考虑在 atoms 与 refs 之间进行权衡,因为它们用于不同的情况。
当您想要原子地更改单个事物时,您应该使用atom。
refs 使用 STM 并涉及在事务中同时更改许多不同的内容。
在您的特定情况下,您应该回答有关您正在更改的内容的问题。
如果您将 旧数据库 切换为 新数据库 并将所有内容一起更改以更改单个字段,因此您说您的数据库是 atom,您'正在滥用机制。
希望区别有所帮助,对于您的示例,我仍然会使用原子。
每个策略背后都有一个good summary here 的动机。
【讨论】: