我在clojure.org/refs看了
所有Refs的读取都将看到“Ref world”的一致快照,作为交易的起点(其“读取点”)。交易将看到它所做的任何更改。这称为in-transaction-value。
维基百科上还有一个指向Snapshot Isolation的链接,暗示一旦交易开始,任何数量的引用的读取都会相互一致。
我做了一个测试案例......
(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
r1 = 3
而不是r1 = 0
的值表明在deref-delay-deref
sleep
之后,<{1}}在之后选择了r1 的值>发生了三次delay-then-inc-ref
次交易。
请注意,在特定交易期间,我知道ensure
阻止更新其他交易的引用,但我不认为这适用于此。只要我看到与交易开始一致的值,我就不在乎ref1
是否会发生变化。
此行为如何与上述文档相符?
答案 0 :(得分:1)
事实证明,如果ref有一些历史记录,它的行为与我期望的一样,所以更改ref声明以添加:min-history
,然后如图所示设置两个refs,似乎可以使它工作.... / p>
(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
1 end
2 start
2 end
3 start
3 end
S r1= 0
S end
nil
阅读here,很明显发生了什么。读取事务正在重新启动,因为在事务启动之前,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")))))
没有历史记录模式的输出:
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
更新:由于测试用例的人为性质,我上面的答案结果令人分心。在实际使用中,事务是否重新启动并不重要,因为必须写入事务以使它们可重新启动。运行时不保证只读事务是否在历史存在/不存在时完成。相反,它可以做任何必要的事情来完成事务的世界,并且必须记住事务代码。更详细的讨论here
我要离开上面作为参考。