在Clojure的Alter vs Commute:我做错了什么?

时间:2016-01-07 19:38:01

标签: clojure transactions

Clojure新手在这里,我正在通过优秀的“从头开始的Clojure”帖子,并尝试了最后一次练习in this post

当我用alter替换commute时,总和是不准确的,但我不明白为什么

(def work (ref (apply list (range 1e5))))
(def sum (ref 0))

(defn trans-alter [work sum]
  (dosync
   (if-let [n (first @work)]
     (do
       (alter work rest)
       (alter sum + n)
       (count @work))
     0)))

(defn trans-commute [work sum]
  (dosync
   (if-let [n (first @work)]
     (do
       (commute work rest)
       (commute sum + n)
       (count @work))
     0)))

(我已经跳过设置期货的代码并将其称为等等)

trans-alter这里,我得到4999950000总和(这是正确的期望值),而trans-commute我每次都得到一个不同的值,但高于预期值(例如4999998211)。

我在这里缺少什么?提前谢谢!

1 个答案:

答案 0 :(得分:5)

虽然通勤在保证正确性方面稍微宽松一点,但通勤和改变基本上都是做同样的事情。

Alter指示STM始终确保此代码一直运行,而没有任何使用的引用从其下面更改。

Commute是一个帮助STM决定何时需要中止事务的指令,因为底层数据从它下面变了。

如果交易中的所有内容都是可交换的,那么即使某些数据发生了变化,也可以让该交易完成 。在您的情况下,两个交易都可以:

  1. 抓住第一个号码
  2. 从工作中移除相同的号码
  3. 将相同的数字添加到结果
  4. 然后使用commute指示STM这是正常的,它应该继续并提交事务...
  5. 得到错误答案。
  6. 因此,简而言之,您要求执行的工作实际上并不是可交换的操作。具体来说,从列表中删除项目不是可交换的。如果您将任何通勤更改为更改,则第4步将踢出其中一个并且仅允许其中一个完成。被踢出的那个将重新运行新的数据,最终会得到正确的结果。