Ref、Var、Agent、Atom間のClojureの違いと例


110

Clojureは初めてですが、実際のシナリオを使って説明してもらえますか。つまり、Ref、Var、Agent、Atomをどこで使用するかです。私は本を​​読みましたが、それでも実際の例は理解できませんでした。

回答:


174

この質問への実際の答えとしては、「The Joy of Clojure」または「programming Clojure」を強くお勧めします。それぞれの動機の短い抜粋を再現できます。

アイデンティティの概念に関するこのビデオを見るか、ここで勉強することから始めます

  • 参照は、「多数のID」への協調同期アクセス用です。
  • アトムは、単一のIDへの調整されていない同期アクセス用です
  • エージェントは、単一のIDへの調整されていない非同期アクセス用です
  • Varは、デフォルト値を共有するスレッドローカル分離ID用です。

協調アクセスは、2つのIDを一緒に変更する必要がある場合に使用されます。典型的な例は、ある銀行口座から別の銀行口座にお金を移動する場合で、完全に移動するか、まったく移動しないかのどちらかです。

非協調アクセスは、1つのIDのみを更新する必要がある場合に使用されます。これは非常に一般的なケースです。

同期アクセスは、すべてのIDが解決してから続行するまでコールが待機することが予想される場合に使用されます。

非同期アクセスは「ファイアアンドフォーゲット」であり、IDが自分の時間内に新しい状態に到達できるようにします。


調整されたアクセスで、のみを変更したいがstate-a、その際に参照したい場合state-b、まだref修正が必要ですか?それで、それは複数のものを変更するのではなく、それらのいずれかを変更するときに複数のものを参照するのですか?
event_jr

2
はい、state-aとstate-bは両方ともrefsでなければならないことを正しく理解しているようです。state-aの新しい値をaとbの値の一貫した組み合わせに基づく場合 その新しい値は、state-aとstate-bが互いに矛盾しないコンテキストで計算される必要があります。それらが両方とも参照である場合、bが途中で変更されると、トランザクションが再開され、aとbの両方の新しい値が使用されます。ensure関数clojure.github.io/clojure/clojure.core-api.html#clojure.core/…を使用して、これを明示的かつ効率的にすることを検討してください。
Arthur Ulfeldt

3
おそらく、デフォルトを共有する分離手段の説明を追加して、答えを完成させることができますか?
Didier A.

1
「協調アクセスは、2つのIDを一緒に変更する必要がある場合に使用されます...」。これを「変更」する必要がありますか?
Carcigenicate 2017

40

参照は、スレッド間で同期する必要がある状態用です。さまざまなものを追跡する必要があり、同時にいくつかのものに書き込む操作を行う必要がある場合は、refsを使用します。複数の異なる状態がある場合は常に、参照を使用することは悪い考えではありません。

アトムは、スレッド間で同期する必要がある独立した状態用です。アトムとその他の状態を同時に変更する必要がない場合は、atアトムを使用しても安全です(特に、プログラム全体で状態の一部が1つしかない場合は、アトムに配置できます)。 。重要な例として、関数の戻り値をキャッシュする(つまり、それをメモする)場合、アトムを使用するとおそらく安全です-状態は関数の外部からは見えないため、心配する必要はありません。関数内の状態変化について、何かを台無しにしています。

エージェントの主なポイントは、別のスレッドで実行されることです。エージェントの値を取得して、その値に関数を適用するように指示することはできますが、関数がいつ実行されるか、または関数がどの値に適用されるかはわかりません。

Varは、スレッドごとに何かを格納する必要がある場合に使用します。マルチスレッドプログラムがあり、各スレッドに独自のプライベート状態が必要な場合は、その状態を変数に入れます。

実際の例では、何をしようとしているのかの例を提供していただければ、何を使用するかを教えてくれます。


32

これらのタイプについて最初に読んだとき、それぞれをどこに使用できるか、またはどこで使用すべきかを理解するのにも苦労したので、ここに簡単な英語の答えがあります。

データが変更されない場合は、varを使用します。あなたが使用するときに発生defし始める機能や、ほとんどdefのようにdefn

変更される単一のアイテムがある場合は、アトムを使用します。例としては、アイテムを追加するカウンターまたはベクターがあります。

同時に変更する必要のあるものが2つ以上ある場合は、refを使用します。慣れているなら「データベーストランザクション」を考えてください。この標準的な例は、ある口座から別の口座への送金です。各アカウントをrefに保存して、変更をアトミックに表示できるようにすることができます。

何かを変更したいが、気にしない場合は、エージェントを使用します。これは、計算に時間がかかる場合や、ファイルやソケットに何かを書き込む場合があります。後者では、を使用する必要があることに注意してくださいsend-off

注:これらのそれぞれにかなりの数があることを感謝しますが、これが出発点になることを願っています。


1
あなたの明確な反応に感謝します:-)私のようなClojureの初心者をかなり助けます。
gosukiwi

27

私はそれらの違いをまとめた記事を書き、どれを使用するか選択するのを助けました。

状態を共有する-いつvars、atoms、agents、refsを使用しますか?

それがそのトピックの答えを探している人々の助けになることを願っています。

@tunaci提案の後の記事からのいくつかのショートカット:

バール

変数はすべてのスレッドに対してグローバルです。

作成後に変数を変更しないでください。技術的には可能ですが、多くの理由で悪い考えです。

原子

スレッドごとに変更可能な状態へのアクセスを共有します。変更は同期的に発生します。実行中に他のスレッドが状態を変更したときに再試行します。

べき等ではない関数や長時間実行される関数を使用しない

エージェント

スレッドごとに変更可能な状態へのアクセスを共有します。変更は非同期で発生します。

参照

Refsはデータベーストランザクションと同様に機能します。dosyncでは、書き込みと読み取りは保護されます。トランザクションで安全な多くの参照を操作できます。

そして、どれを使用するかのフローチャート: フローチャート

一部の更新は常に可能ですので、ウェブサイトの画像をご覧ください。

コピーと過去の記事なしで完全な答えを出すのは複雑で長いトピックなので、ウェブサイトにリダイレクトするのを許してください:)


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.