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

Multithreading 如何以线程安全的方式初始化atom?,multithreading,concurrency,clojure,Multithreading,Concurrency,Clojure,假设我有一个原子: (def my-atom (atom nil)) 然后我将其初始化如下: (defn init-atom [init-value] (when (nil? @my-atom) (reset! my-atom init-value))) (defn init-atom [produce-init-fn] (when (nil? @my-atom) (reset! my-atom (produce-init-fn)]))) 若init atom是从不

假设我有一个原子:

(def my-atom (atom nil))
然后我将其初始化如下:

(defn init-atom [init-value]
  (when (nil? @my-atom)
    (reset! my-atom init-value)))
(defn init-atom [produce-init-fn]
  (when (nil? @my-atom)
    (reset! my-atom (produce-init-fn)])))
若init atom是从不同的线程并发调用的,那个么可能会发生I争用情况。我正在寻找一种安全、正确地初始化原子的方法。有什么事吗

UPD:

实际上,我正在初始化它,如下所示:

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

product init fn可能包含副作用。

这应该可以做到:

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

这应该可以做到:

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

以下内容将确保atom仅初始化一次:

(defn init-atom [init-value]
  (swap! my-atom #(when (nil? %) init-value)))
原子和交换!语义保证函数传递给swap!将以原子方式执行

如果您传递一个生成init值的函数,那么它将不能作为交换!如果事务发生冲突,可能会多次调用该函数。然后,您需要使用某种锁定,如另一个答案中所示:

(let [o (Object.)]
  (defn init-atom [init-value-fn]
    (locking o
      (swap! my-atom #(when (nil? %) (init-value-fn))))))
如果我的atom还有其他并发事务,那么init值fn仍可能被多次调用

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

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

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

以下内容将确保atom仅初始化一次:

(defn init-atom [init-value]
  (swap! my-atom #(when (nil? %) init-value)))
原子和交换!语义保证函数传递给swap!将以原子方式执行

如果您传递一个生成init值的函数,那么它将不能作为交换!如果事务发生冲突,可能会多次调用该函数。然后,您需要使用某种锁定,如另一个答案中所示:

(let [o (Object.)]
  (defn init-atom [init-value-fn]
    (locking o
      (swap! my-atom #(when (nil? %) (init-value-fn))))))
如果我的atom还有其他并发事务,那么init值fn仍可能被多次调用

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

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

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

为什么需要从不同的线程并发调用init atom?更重要的是,你为什么要使用呢?在我的领域中,这个atom可以从不同的线程访问缓存。因此,首先要检查是否有任何东西。如果什么都没有,那就把价值放在那里@原子初始化之后,它的值还会改变吗?换句话说,所谓缓存,您是指像中使用的atom,还是像只接收一次值的a?它就像memoize,例如,可能有多个写入到atom@SamestepwWhy您需要从不同线程并发调用init atom?更重要的是,你为什么要使用呢?在我的领域中,这个atom可以从不同的线程访问缓存。因此,首先要检查是否有任何东西。如果什么都没有,那就把价值放在那里@原子初始化之后,它的值还会改变吗?换句话说,所谓缓存,你是指像中使用的atom,还是像只接收一次值的a?它类似于memoize,例如,可能会多次写入atom@Samestepac。实际上,我调用一个函数来生成init值。很抱歉,初始问题不正确。我已更新我的答案,以涵盖您在问题中所做的更改。您的上一个版本只是使用延迟来重新实施延迟。最好只定义我的atom delay init值fn,并且永远不要调用init atom。如果您假设以后不会有其他代码更改atom值,这是正确的。实际上,我正在调用一个函数来生成init值。很抱歉,初始问题不正确。我已更新我的答案,以涵盖您在问题中所做的更改。您的上一个版本只是使用延迟来重新实施延迟。最好定义我的atom delay init值fn,并且永远不要调用init atom。如果您假设以后不会有其他代码更改atom值,那么这是正确的。