使用代理来完成STM事务中的副作用

mik*_*era 8 concurrency transactions clojure agent stm

我知道在STM事务中放置具有副作用的函数通常是不好的做法,因为它们可能被重试并多次调用.

然而,我发现你可以使用代理来确保只有在事务成功完成后才能执行副作用.

例如

(dosync
  // transactional stuff
  (send some-agent #(function-with-side-effects params))
  // more transactional stuff
  )
Run Code Online (Sandbox Code Playgroud)

这是好习惯吗?

有哪些优点/缺点/陷阱?

Ale*_*ler 7

原版的:

似乎这应该对我有用.根据您的副作用,您可能希望使用send-off(对于IO绑定的操作)而不是send(对于cpu绑定的操作).发送/发送将把任务排入其中一个内部代理程序执行程序池(对于cpu有一个固定大小的池,对于io ops有一个无限大小的池).一旦任务入队,该工作将脱离dosync的线程,因此您在此时断开连接.

您需要在事务中将所需的任何值捕获到发送的函数中.并且您需要处理由于重试而可能多次发送的发送.

更新(见评论):

在ref的事务中发送的代理发送将保持到ref事务成功完成并执行一次.所以在我上面的回答中,发送不会多次发生,但是在ref事务期间不会发生,这可能不是你想要的(如果你希望记录或做有效的东西).


cgr*_*and 5

这是有效的,也是常见做法.然而,像亚历克斯正确地指出你应该考虑发送超过发送.

有更多方法可以捕获提交值并将其从事务中移出.例如,您可以在向量(或地图或其他)中返回它们.

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

或者你可以打电话重置!在本地原子上(当然在dosync块的词法范围之外定义).