如何让长时间运行的事务在clojure中快速失败

时间:2014-08-09 03:24:03

标签: clojure

假设以下代码中的ref在其他事务以及下面的事务中被修改, 我担心的是这个事务会一直运行,直到它提交时间,提交失败,然后重新运行事务。

(defn modify-ref [my-ref]
  (dosync (if (some-prop-of-ref-true @my-ref)
            (alter my-ref long-running-calculation))))

这是我的恐惧:

  1. 调用modify-ref,启动事务(称之为A),并开始长时间运行计算
  2. 另一个事务(称之为B)启动,修改my-ref,并返回(提交成功)
  3. 长时间运行计算一直持续到完成
  4. 事务A尝试提交但由于my-ref已被修改而失败
  5. 使用my-ref的新值重新启动事务(称之为A')并退出,因为some-prop不是真的
  6. 这就是我想要发生的事情,也许这就是发生的事情(我只是不知道,所以我问这个问题: - )

    当事务B提交my-ref时,我希望事务A立即停止(因为my-ref的值已经改变)并重新启动新值。这是怎么回事?

    我想要这种行为的原因是长时间运行计算不会浪费所有CPU时间用于现在过时的计算。

    我考虑过使用ensure,但我不确定如何在此上下文中使用它,或者是否有必要。

2 个答案:

答案 0 :(得分:2)

你可以随心所欲地工作。

停止JVM中的一个线程,无论它在做什么都需要协同工作,因此Clojure(或任何其他JVM语言)没有通用的方法来阻止正在运行的计算。计算必须定期检查信号,看它是否应该自行停止。请参阅How do you kill a thread in Java?

关于如何实现它,我会说这太难了,所以如果它真的是一个问题我会首先测量。如果是,我会看到传统的pessimistic lock是否是更好的解决方案。如果悲观锁仍然不是解决方案,我会尝试构建在事务之外运行计算的东西,在refs上使用watchers并在计算后有条件地设置refs,如果它们仍然具有相同的值。当然,这在事务边界之外运行,听起来可能更加棘手。

关于ensure,只有正在修改的引用参与交易,因此您可能会受到write skew的影响。有关详细说明,请参阅Clojure STM ambiguity factor

答案 1 :(得分:1)

这不会发生,因为......好吧,怎么可能呢?您的函数long-running-calculation中没有任何代码可以过早地处理停止,并且该代码是您要取消事务时运行的代码。因此,阻止它的唯一方法是抢先停止线程执行并在其他位置强制重新启动它。这非常危险,因为在Java 1.1中发现了java.lang.Tread / stop;副作用可能比一些浪费的CPU周期更糟糕。

refs尝试解决这个问题,排序:如果有一个长时间运行的事务必须多次重启,因为较短的事务一直在偷偷摸摸,它将需要更强的锁定并运行完成。但这种情况非常罕见(哎呀,甚至需要使用refs也很少见,这对于refs表现来说是一种罕见的方式)。