Concurrency 使用代理完成STM交易中的副作用
我知道,将具有副作用的函数放在STM事务中通常是不好的做法,因为它们可能会被重试和多次调用 然而,我突然想到,您可以使用代理来确保只有在事务成功完成后才能执行副作用 e、 g 这是好的做法吗Concurrency 使用代理完成STM交易中的副作用,concurrency,clojure,transactions,agent,stm,Concurrency,Clojure,Transactions,Agent,Stm,我知道,将具有副作用的函数放在STM事务中通常是不好的做法,因为它们可能会被重试和多次调用 然而,我突然想到,您可以使用代理来确保只有在事务成功完成后才能执行副作用 e、 g 这是好的做法吗 优点/缺点/缺点是什么?原文: 这对我来说应该行得通。根据副作用的不同,您可能希望使用发送(用于IO绑定操作)而不是发送(用于cpu绑定操作)。发送/发送将把任务排队到一个内部代理执行器池中(cpu有一个固定大小的池,io操作有一个无限大小的池)。一旦任务进入队列,工作就脱离了dosync的线程,因此此时就
优点/缺点/缺点是什么?原文: 这对我来说应该行得通。根据副作用的不同,您可能希望使用发送(用于IO绑定操作)而不是发送(用于cpu绑定操作)。发送/发送将把任务排队到一个内部代理执行器池中(cpu有一个固定大小的池,io操作有一个无限大小的池)。一旦任务进入队列,工作就脱离了dosync的线程,因此此时就断开了连接 当然,您需要将事务中需要的任何值捕获到sent函数中。您需要处理由于重试而可能多次发生的发送 更新(见评论):
ref事务中的代理发送被保留,直到ref事务成功完成并执行一次。因此,在我上面的回答中,发送不会发生多次,但是它不会在ref事务期间发生,而ref事务可能不是您想要的(如果您希望记录日志或做一些副作用的事情)。这很有效,并且是常见的做法。但是,正如亚历克斯正确指出的,你应该考虑发送。 有更多的方法可以捕获提交的值并将其从事务中分发出去。例如,您可以在向量(或地图或其他任何形式)中返回它们
或者你可以呼叫重置!在本地原子上(当然,定义在dosync块的词法范围之外)。使用代理没有错,但是仅仅从影响计算的事务返回值通常就足够了 REF可能是最干净的方法,但是你甚至可以用原子来管理它
(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"))))
(定义工作队列大小(原子[0]))
(定义添加作业[thunk]
(让[[运行已接受?]
(交换!工作队列大小)
(fn[[现行]]
(如果(
交换
可以根据需要重试多次,但工作队列永远不会超过三次,并且您将始终只打印一次与接受工作项正确关联的消息。“原始设计”要求原子中只有一个int,但你可以将它转换成一对,以便将有趣的数据从计算中传递回来。STM的核心思想之一是失败原子性。这有什么帮助呢?重点是交易成功后需要发生的副作用,但这些副作用本身不是交易的一部分,例如发送确认电子邮件。显然,您不希望每次事务重试时都这样做,否则您可能会得到一个非常生气/困惑的收件人@mikera,@Alex,发送保证只在事务成功后发生一次,不应该发生多次。(声明5中)@bmillare-我想你在谈论其他事情。该语句涉及如何执行发送/发送中传递的函数。我想说的是,通过在ref事务中进行调用,ref事务可能会被重试,从而导致多个发送/发送调用,每个调用都只执行一次。@alex,对不起,我在文本中引用了错误的点,进一步说“代理与STM集成-事务中的任何调度都会一直保持到提交为止,如果重试或中止,则会被丢弃。”。“@bmillare-hmmm…”我明白你的意思。我仍然不确定我是否同意会发生这种情况,因为我不知道ref事务中的send会有相同的约束。也许是时候写一点代码来找出…@bmillare的确,你是对的。从ref事务中发送的代理将在成功的ref事务结束时保留并执行。所以,@mikera我上面最初的回答是不正确的。
(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"))))