8

副作用のある関数を STM トランザクション内に配置することは、一般的に悪い習慣であることを認識しています。これらの関数は、再試行され、複数回呼び出される可能性があるためです。

ただし、エージェントを使用して、トランザクションが正常に完了した後にのみ副作用が実行されるようにすることができると思います。

例えば

(dosync
  // transactional stuff
  (send some-agent #(function-with-side-effects params))
  // more transactional stuff
  )

これは良い習慣ですか?

長所/短所/落とし穴は何ですか?

4

3 に答える 3

8

オリジナル:

それは私にはうまくいくはずです。副作用の内容によっては、send (CPU バウンド ops の場合) の代わりに send-off (IO-bound ops の場合) を使用することをお勧めします。send/send-off は、タスクを内部エージェント エグゼキューター プールの 1 つにキューに入れます (CPU 用の固定サイズ プールと、io ops 用の無制限サイズ プールがあります)。タスクがキューに入れられると、作業は dosync のスレッドから外れるため、その時点で切断されます。

もちろん、トランザクション内から送信された関数に必要な値をキャプチャする必要があります。また、再試行のために複数回発生する可能性があるその送信に対処する必要があります。

更新(コメントを参照):

ref のトランザクション内で送信されたエージェントは、ref トランザクションが正常に完了するまで保持され、1 回実行されます。したがって、上記の私の回答では、送信は複数回発生しませんが、ref トランザクション中には発生しない可能性があります。

于 2011-01-22T17:07:58.127 に答える
6

これは機能し、一般的な方法です。ただし、アレックスが正しく指摘したように、送信よりも送信を検討する必要があります。

コミットされた値を取得してトランザクションから渡す方法は他にもあります。たとえば、それらをベクトル (またはマップなど) で返すことができます。

(let [[x y z] (dosync
                ; do stuff
                [@x @y @z])] ; values of interest to sode effects
  (side-effect x y z))

または、リセットを呼び出すことができます! ローカル アトム (もちろん、dosync ブロックのレキシカル スコープの外で定義されます)。

于 2011-01-23T17:23:45.630 に答える
1

エージェントを使用することに問題はありませんが、多くの場合、副作用のある計算に必要な値をトランザクションから返すだけで十分です。

参照はおそらくこれを行うための最もクリーンな方法ですが、アトムだけで管理することもできます!

(def work-queue-size (atom [0]))

(defn add-job [thunk]
  (let [[running accepted?]
        (swap! work-queue-size
               (fn [[active]]
                 (if (< active 3)
                   [(inc active) true]
                   [active false])))]
    (println
     (str "Your job has been "
          (if accepted?
            "queued, and there are "
            "rejected - there are already ")
          running
          " total running jobs"))))

swap!必要に応じて何度でも再試行できますが、ワーク キューが 3 つを超えることはなく、ワーク アイテムの受け入れに正しく関連付けられたメッセージを常に1 回だけ出力します。「元の設計」では、atom に 1 つの int のみが必要でしたが、興味深いデータを計算から戻すためにペアにすることができます。

于 2011-05-12T16:21:18.827 に答える