假设我有一个原子:
(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
可能包含副作用。
答案 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)))))