2016-08-18 45 views
1

比方说,我有一个原子:如何以线程安全的方式初始化原子?

(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

为什么你需要从不同的线程同时调用'init-atom'?更重要的是,为什么你会使用[global state](http://programmers.stackexchange.com/q/148108)呢? –

+0

在我的域中,这个原子可以从不同的线程访问缓存。所以首先检查是否有任何东西。如果没有 - 在那里提供价值。 @SamEstep – OlegTheCat

+0

原子初始化后,它的值会在那之后改变吗?换句话说,通过“缓存”,你的意思是像['memoize'](https://clojuredocs.org/clojure.core/memoize)中使用的原子,或者像['promise'](https:/ /clojuredocs.org/clojure.core/promise)只收到一次值? –

回答

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))) 
+0

实际上,我正在调用一个函数来产生init值。对不起,初始问题不正确。 – OlegTheCat

+0

我已更新我的答案以涵盖您在问题中所做的更改。 –

+0

你的上一个版本只是使用'delay'来重新实现'delay'。也可能只是'(def my-atom(delay init-value-fn))'并且从不调用'init-atom'。 – amalloy

1

这应该做的伎俩:

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