STMトランザクションで副作用を完了するためにエージェントを使用します
-
16-10-2019 - |
質問
STMトランザクション内に副作用を備えた関数を配置することは一般に悪い習慣であることを知っています。
ただし、エージェントを使用して、トランザクションが正常に完了した後にのみ副作用が実行されるようにすることができます。
例えば
(dosync
// transactional stuff
(send some-agent #(function-with-side-effects params))
// more transactional stuff
)
これは良い練習ですか?
長所/短所/落とし穴は何ですか?
解決
オリジナル:
それが私にはうまくいくはずのようです。副作用が何であるかに応じて、送信(CPUバウンドOPSの場合)の代わりに(IOバウンドOPSの場合)SENDOFFを使用することをお勧めします。送信/送信は、タスクを内部エージェントエグゼキュータープールの1つにエンキューします(CPU用の固定サイズプールとIO OPSにはバウンドされていないサイズプールがあります)。タスクがエンキューされると、作業はDosyncのスレッドから外れているため、その時点で切断されます。
もちろん、トランザクション内から送信された関数への必要な値をキャプチャする必要があります。そして、あなたは再試行のために複数回発生する可能性のある送信に対処する必要があります。
更新(コメントを参照):
Refのトランザクション内でエージェント送信は、Refトランザクションが正常に完了し、1回実行されるまで保持されます。したがって、上記の私の回答では、送信は複数回発生しませんが、参照トランザクション中には発生しません。
他のヒント
これは機能し、一般的な慣行です。しかし、アレックスが正しく指摘したように、あなたは送信を超えることを検討する必要があります。
コミットされた価値をキャプチャし、それらをトランザクションから渡す方法は他にもあります。たとえば、ベクトル(またはマップなど)でそれらを返すことができます。
(let [[x y z] (dosync
; do stuff
[@x @y @z])] ; values of interest to sode effects
(side-effect x y z))
または、リセットを呼び出すことができます!局所原子(コースのドシンブロックの語彙範囲外で定義)。
エージェントの使用には何の問題もありませんが、副作用の計算に必要なトランザクション値から戻るだけで十分です。
参照はおそらくこれを行うための最もクリーンな方法ですが、ただ原子でそれを管理することもできます!
(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つ以上大きくなることはありません。 一度だけ あなたの仕事アイテムの受け入れに正しく結び付けられているメッセージ。 「オリジナルのデザイン」では、原子の単一のINTのみが必要でしたが、興味深いデータを計算から渡すためにペアに変えることができます。