Clojure 在STM事务中引用是否真的一致?
我读了那本书 从事务的起点(其“读取点”)起,所有Ref读取都将看到“Ref世界”的一致快照。事务将看到它所做的任何更改。这称为事务内值 维基百科上还有一个指向的链接,这意味着一旦事务开始,对任意数量的引用的读取将彼此一致 我做了一个测试用例Clojure 在STM事务中引用是否真的一致?,clojure,stm,Clojure,Stm,我读了那本书 从事务的起点(其“读取点”)起,所有Ref读取都将看到“Ref世界”的一致快照。事务将看到它所做的任何更改。这称为事务内值 维基百科上还有一个指向的链接,这意味着一旦事务开始,对任意数量的引用的读取将彼此一致 我做了一个测试用例 (def r1 (ref 0)) (def r2 (ref 0)) (defn delay-then-inc-ref [id ref delay] (.start (Thread. #((println id " star
(def r1 (ref 0))
(def r2 (ref 0))
(defn delay-then-inc-ref [id ref delay]
(.start
(Thread.
#((println id " start")
(Thread/sleep delay)
(dosync
(alter ref inc))
(println id " end")))))
(defn deref-delay-deref [ref1 ref2 delay]
(.start
(Thread.
#((println "S start")
(dosync
(let [a @ref2]
(Thread/sleep delay)
(println "S r1=" @ref1))) ; @ref1 consistent with @ref2 ?
(println "S end")))))
*clojure-version*
;=> {:major 1, :minor 3, :incremental 0, :qualifier nil}
(deref-delay-deref r1 r2 2000)
(delay-then-inc-ref "1" r1 500)
(delay-then-inc-ref "2" r1 1000)
(delay-then-inc-ref "3" r1 1500)
输出为:
S start
1 start
2 start
3 start
1 end
2 end
3 end
r1 = 3
S end
nil
S start
1 start
1 end
2 start
2 end
3 start
3 end
S r1= 0
S end
nil
r1=3
的值而不是r1=0
表明,在deref delay deref
中,在sleep
之后的ref1的deref是在三个delay-then inc ref
事务发生后拾取r1的值
请注意,我知道如何确保在特定事务期间防止其他事务更新引用,但我认为这不适用于这里。只要看到与交易开始时一致的值,我不在乎ref1
是否更改
这种行为如何与上述参考文档相匹配?事实证明,如果ref有一些历史记录,它的行为与我预期的一样,因此更改ref声明以添加一个
:min history
,然后如图所示设置两个ref,似乎可以使其正常工作
(def r1 (ref 0 :min-history 5))
(def r2 (ref 0 :min-history 5))
(dosync
(ref-set r1 0)
(ref-set r2 0))
那么输出是:
S start
1 start
2 start
3 start
1 end
2 end
3 end
r1 = 3
S end
nil
S start
1 start
1 end
2 start
2 end
3 start
3 end
S r1= 0
S end
nil
阅读,很清楚发生了什么。读取事务正在重新启动,因为事务启动之前的ref历史记录中没有条目。为了确认,我添加了更多日志记录:
(defn deref-delay-deref [ref1 ref2 delay]
(.start
(Thread.
#((println "S start")
(dosync
(println "transaction starting")
(let [a @ref2]
(Thread/sleep delay)
(println "S r1=" @ref1))) ; should be consistent with @ref2
(println "S end")))))
不带历史记录mods的输出:
S start
transaction starting
1 start
2 start
3 start
1 end
2 end
3 end
transaction starting
S r1= 3
S end
S start
transaction starting
1 start
2 start
3 start
1 end
2 end
3 end
S r1= 0
S end
nil
使用历史mods:
S start
transaction starting
1 start
2 start
3 start
1 end
2 end
3 end
transaction starting
S r1= 3
S end
S start
transaction starting
1 start
2 start
3 start
1 end
2 end
3 end
S r1= 0
S end
nil
更新:事实证明,由于测试用例的人为性质,我上面的答案有些让人分心。在实际使用中,事务是否重新启动并不重要,因为事务必须以可重新启动的方式写入。运行时不保证在有/无历史记录的情况下是否完成只读事务。相反,它可以做任何必要的事情来完成事务世界,而编写事务代码时必须牢记这一点。更详细的讨论
我将以上内容留作参考。因为我回答了自己的问题,所以根据meta.stackoverflow.com的礼仪建议,我将此答案标记为社区维基