0

假设以下代码中的 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 不正确

这就是我想要发生的事情,也许这就是发生的事情(我只是不知道,所以我在问这个问题:-)

当事务 B 提交 my-ref 时,我希望事务 A 立即停止(因为 my-ref 的值已更改)并使用新值重新启动。是这样吗?

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

我考虑过使用ensure,但我不确定如何在这种情况下使用它或者是否有必要。

4

2 回答 2

3

它就像你害怕的那样工作。

停止 JVM 中的线程正在做的任何事情都需要协作,因此 Clojure(或任何其他 JVM 语言)没有通用的方法来停止正在运行的计算。计算必须定期检查信号以查看它是否应该自行停止。请参阅如何在 Java 中终止线程?.

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

About ensure,只有被修改的 refs 参与事务,所以你可能会遭受write skew。有关详细说明,请参阅Clojure STM 歧义因子

于 2014-08-09T12:26:59.413 回答
2

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

refs 确实试图解决这个问题,排序:如果有一个长时间运行的事务必须多次重新启动自己,因为较短的事务不断潜入,它将需要更强的锁定并运行到完成。但这是非常罕见的情况(见鬼,即使需要使用 refs 也是很少见的,这是 refs 的一种罕见行为方式)。

于 2014-08-09T12:28:51.010 回答