2016-08-18 7 views
1

はのは、私が原子を持っているとしましょう:原子をスレッドセーフな方法で初期化するには?

(def my-atom (atom nil)) 

次のようにその後、私はそれを初期化:

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

init-atomが異なるスレッドから同時に呼び出された場合、I競合状態が発生することがあります。私は安全かつ正確に原子を初期化する方法を探しています。そこに何か?

UPD:次のように

は実際に私がそれを初期化しています:

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

produce-init-fnは副作用が含まれていてもよいです。

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

異なるスレッドから 'init-atom'を同時に呼び出す必要があるのはなぜですか?さらに重要なのは、どうしてあなたは[global state](http://programmers.stackexchange.com/q/148108)を使うのですか? –

+0

私のドメインでは、このアトムは異なるスレッドからアクセス可能なキャッシュです。だから最初に何かがあるかどうかを調べる。何もなければ、そこに価値を置く。 @SamEstep – OlegTheCat

+0

原子が初期化されると、その値はその後変更されますか?言い換えれば、 "cache"によって、あなたは[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))) 

アトムとswap!セマンティクスがswap!に渡された関数があることを保証

+0

実際、私はinitの値を生成する関数を呼び出しています。申し訳ありませんが、初期の質問が間違っています。 – OlegTheCat

+0

質問の変更をカバーするために私の回答を更新しました。 –

+0

あなたの最後のバージョンは単に 'delay'を使って' delay'を再実装しています。 '(def my-atom(delay init-value-fn)') 'と' init-atom'を全く呼び出さないでください。 – amalloy

1

これはトリックを行う必要があります原子的に実行される。

init値を生成する関数を渡すと、スワップとしては機能しません!競合するトランザクションが発生した場合に複数回機能を呼び出すことがあります。そして、あなたは他の回答のように、ロックのいくつかの種類を使用する必要があります。my-atomと他の同時トランザクションがある場合

(let [o (Object.)] 
    (defn init-atom [init-value-fn] 
    (locking o 
     (swap! my-atom #(when (nil? %) (init-value-fn)))))) 

init-value-fnはまだ複数回呼び出されることがあります。

あなたは遅延初期化とinit-value-fnをサポートする必要がある場合はあなただけdelayにそれをラップすることができますすべてのスレッドのための先行投資と同じ知られているし、それは一度だけ呼び出され、その結果がキャッシュされ、再利用されます。

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

(defn init-atom [] 
    (swap! my-atom #(when (nil? %) @my-init-value))) 
関連する問題