28

サイトで入手できる API とドキュメントから Clojure を学ぼうとしています。Clojure の変更可能なストレージについては少しよくわかりません。私の理解が正しいことを確認したいと思います。私が間違っているという考えがあれば教えてください。

編集:その正確性についてコメントを受け取ったので、これを更新しています。


免責事項: この情報はすべて非公式であり、間違っている可能性があります。Clojure の仕組みを理解するためにこの記事を使用しないでください。


Varsには常にルート バインディングが含まれ、場合によってはスレッドごとのバインディングも含まれます。これらは命令型言語の通常の変数に相当し、スレッド間で情報を共有するのには適していません。(アーサー・ウルフェルトに感謝)

参照は、単一のトランザクションで任意の数の参照の状態を変更できるアトミック トランザクションをサポートするスレッド間で共有される場所です。トランザクションは同期式 (dosync) の終了時にコミットされ、競合は STM マジック (ロールバック、キュー、待機など) で自動的に解決されます。

エージェントは、独立したアクション関数をディスパッチしてエージェントの状態を変更することにより、最小限のオーバーヘッドでスレッド間で情報を非同期的に共有できるようにする場所です。エージェントはすぐに返されるため、非ブロッキングですが、ディスパッチされた関数が完了するまでエージェントの値は設定されません。

アトムは、スレッド間で同期的に共有できる場所です。異なるスレッド間の安全な操作をサポートします。

これらの構造をいつ使用するかに基づいた私のわかりやすい要約は次のとおりです。

  • Var は、命令型言語の通常の古い変数のようなものです。(可能な限り避けてください)
  • Atom は Var に似ていますが、即時の読み取りと安全な設定を可能にするスレッド共有の安全性を備えています。(ありがとうマーティン)
  • エージェントは Atom に似ていますが、ブロックするのではなく、新しいスレッドを生成してその値を計算し、値の変更中にのみブロックし、割り当てが完了したことを他のスレッドに知らせることができます。
  • Ref は、トランザクションで自分自身をロックする共有の場所です。ロックされたコードのすべての部分について競合状態で何が起こるかをプログラマーに決定させる代わりに、トランザクションを開始し、Clojure にそのトランザクション内の参照間のすべてのロック状態を処理させます。

また、関連する概念として function がありますfuture。私には、将来のオブジェクトは、計算が完了するまで値にまったくアクセスできない同期エージェントとして記述できるように思えます。ノンブロッキング Atom と表現することもできます。これらは正確な未来の概念ですか?

4

5 に答える 5

6

本当にClojureを手に入れているようですね!よくできた :)

Vars には、すべてのスレッドで表示される「ルート バインディング」があり、個々のスレッドは、他のスレッドに影響を与えることなく、表示される値を変更できます。私の理解が正しければ、変数は、すべてに表示されるルートバインディングなしで1つのスレッドに存在することはできず、最初に (def ... ) で定義されるまで「再バインド」できません。

参照は、変更を含む (dosync ... ) トランザクションの最後にコミットされますが、トランザクションが一貫した状態で終了できた場合のみです。

于 2009-06-22T17:13:46.423 に答える
4

Atoms に関するあなたの結論は間違っていると思います。

Atom は Var に似ていますが、値が変更されるまでブロックするスレッド共有の安全性を備えています。

アトムはswap!または で低レベルに変更されcompare-and-set!ます。これは何もブロックしません。swap!ref が 1 つだけのトランザクションのように機能します。

  1. 古い値はアトムから取得され、スレッドローカルに格納されます
  2. 関数が古い値に適用され、新しい値が生成されます
  3. これが成功すると、compare-and-set が古い値と新しい値で呼び出されます。アトムの値が他のスレッドによって変更されていない (古い値と等しい) 場合にのみ、新しい値が書き込まれます。
于 2009-06-24T09:04:09.033 に答える
3

あなたの質問には 2 つの問題が見つかりました。

あなたは言う:

アクションの発生中にエージェントがアクセスされた場合、アクションが完了するまで値は返されません

http://clojure.org/agentsは次のように述べています。

エージェントの状態は、どのスレッドでもすぐに読み取ることができます

つまり、エージェントの値を取得するのを待つ必要はありません (アクションによって変更された値はプロキシされ、アトミックに変更されると想定しています)。

derefan の -methodのコードはAgent次のようになります (SVN リビジョン 1382):

public Object deref() throws Exception{
    if(errors != null)
    {
        throw new Exception("Agent has errors", (Exception) RT.first(errors));
    }
return state;

}

ブロッキングは含まれません。

また、私はあなたが何を意味するのか理解できません(参照セクションで)

トランザクションは deref の呼び出しでコミットされます

dosync ブロックのすべてのアクションが完了し、例外がスローされず、トランザクションが再試行される原因が何もない場合、トランザクションはコミットされます。私はそれとderefは何の関係もないと思いますが、あなたの主張を誤解しているかもしれません。

于 2009-06-22T20:57:23.673 に答える
1

Atoms の操作は 1 から再開するという Martin の言葉は正しいです。最終的に成功するまでは。スピン待ちともいう。ロックで実際にブロックすることはありませんが、操作を実行したスレッドは操作が成功するまでブロックされるため、これはブロック操作であり、非同期操作ではありません。

Futures についても、Clojure 1.1 は promise と futures の抽象化を追加しました。promise は、あるスレッドから別のスレッドに値を配信するために使用できる同期構造です。値が配信されるまで、promise を逆参照しようとするとブロックされます。

(def a-promise (promise))
(deliver a-promise :fred)

Future は非同期計算を表します。これらは、別のスレッドで実行するコードを取得し、結果を取得する方法です。

(def f (future (some-sexp)))
(deref f) ; blocks the thread that derefs f until value is available
于 2010-02-22T23:20:19.160 に答える
0

Vars には常にルート バインディングがあるとは限りません。を使用してバインディングなしで var を作成することは合法です

(def x)

また

(declare x)

値を持つ前に x を評価しようとすると、次の結果になります。

Var user/x is unbound.
[Thrown class java.lang.IllegalStateException]
于 2010-09-18T22:00:24.500 に答える