当我有条件地转到swap!
原子的值时,条件应该包裹swap!
还是应该成为函数swap!
调用的一部分?
(import '(java.time Instant))
(def not-nil? (comp not nil?))
(defonce users (atom {
"example user 1" {:ts (Instant/now)}
"example user 2" {:ts (Instant/now)}
}))
(defn update-ts [id]
(if (not-nil? (get @users id))
(swap! users assoc-in [id :ts] (Instant/now))))
在上面的示例中,我在执行swap!
之前对用户进行了存在检查。但是,检查后但users
之前,用户是否无法从swap!
删除?那么,将检查放在由swap!
运行的函数中是否更安全?
(defn update-ts [id]
(swap! users (fn [users]
(if (not-nil? (get users id))
(assoc-in users [id :ts] (Instant/now))
users))))
答案 0 :(得分:7)
但是在检查之后但
swap!
之前,用户无法删除用户?那么,将检查放在由swap!
运行的函数中是否更安全?
是的,完全正确。你永远不应该决定如何在原子上swap!
内的任何地方变异原子。因为swap!
是唯一保证是原子的操作,所以每次你做其他事情(即,从swap!
之外做出关于原子的决定)时,你会引入竞争条件。
答案 1 :(得分:3)
但检查后用户无法从用户中删除用户,但是 在交换之前!?那么,把支票放在里面是否更安全 函数由swap运行!?
正如amalloy所说,如果你需要它是bulletproot,你必须将not-null?
检查放在交换功能中。
但是,请记住,你和&你的团队正在编写程序的其余部分。因此,您有很多可以简化决策的外部信息:
如果你只有一个线程(就像大多数程序一样),你永远不必担心竞争状况。
如果您有2个或更多线程,也许您永远不会从地图中删除条目(它只会累积:ts
个值)。那么你也不必担心冲突。
如果您的函数比上面的简单示例更复杂,您可能希望使用(dosync ...)
表单来包装多个步骤,而不是将所有内容整合到一个swap
函数中。< / p>
在第三种情况下,将atom
替换为ref
。一个例子是:
(defonce users (ref {...} )) ; ***** must us a ref here *****
(dosync
(if (not-nil? (get @users id))
<lots of other stuff...>
(alter users assoc-in [id :ts] (Instant/now)))))