我应该在交换之外或之内运行支票!功能?

时间:2017-06-12 23:01:08

标签: clojure

当我有条件地转到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))))

2 个答案:

答案 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)))))