Concurrency 使用代理完成STM交易中的副作用

Concurrency 使用代理完成STM交易中的副作用,concurrency,clojure,transactions,agent,stm,Concurrency,Clojure,Transactions,Agent,Stm,我知道,将具有副作用的函数放在STM事务中通常是不好的做法,因为它们可能会被重试和多次调用 然而,我突然想到,您可以使用代理来确保只有在事务成功完成后才能执行副作用 e、 g 这是好的做法吗 优点/缺点/缺点是什么?原文: 这对我来说应该行得通。根据副作用的不同,您可能希望使用发送(用于IO绑定操作)而不是发送(用于cpu绑定操作)。发送/发送将把任务排队到一个内部代理执行器池中(cpu有一个固定大小的池,io操作有一个无限大小的池)。一旦任务进入队列,工作就脱离了dosync的线程,因此此时就

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

然而,我突然想到,您可以使用代理来确保只有在事务成功完成后才能执行副作用

e、 g

这是好的做法吗


优点/缺点/缺点是什么?

原文:

这对我来说应该行得通。根据副作用的不同,您可能希望使用发送(用于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"))))