如何以线程安全的方式初始化atom?

时间:2016-08-18 13:58:49

标签: multithreading concurrency clojure

假设我有一个原子:

(def my-atom (atom nil))

然后我按如下方式初始化它:

(defn init-atom [init-value]
  (when (nil? @my-atom)
    (reset! my-atom init-value)))

如果从不同的线程同时调用init-atom,则可能发生竞争条件。我正在寻找一种安全正确地初始化原子的方法。还有什么吗?

UPD:

其实我正在按如下方式初始化它:

(defn init-atom [produce-init-fn]
  (when (nil? @my-atom)
    (reset! my-atom (produce-init-fn)])))

produce-init-fn可能包含副作用。

2 个答案:

答案 0 :(得分:3)

以下内容将确保原子只初始化一次:

(defn init-atom [init-value]
  (swap! my-atom #(when (nil? %) init-value)))

Atom和swap!语义保证传递给swap!的函数将以原子方式执行。

如果你传递一个产生init值的函数,那么它就不会作为交换工作!如果事务冲突,可能会多次调用该函数。然后你需要像其他答案一样使用某种锁定:

(let [o (Object.)]
  (defn init-atom [init-value-fn]
    (locking o
      (swap! my-atom #(when (nil? %) (init-value-fn))))))
如果有init-value-fn的其他并发交易,

my-atom仍可能被多次调用。

如果你需要支持延迟初始化并且init-value-fn已经预先知道并且对于所有线程都是相同的,你可以将它包装到delay然后它只会被调用一次,其结果将是被缓存和重用:

(def my-init-value (delay init-value-fn))

(defn init-atom []
  (swap! my-atom #(when (nil? %) @my-init-value)))

答案 1 :(得分:1)

这应该可以解决问题:

(let [o (Object.)]
  (defn init-atom [init-value]
    (locking o
      (when (nil? @my-atom)
        (reset! my-atom init-value)))))