Are refs really consistent within a STM transaction?
我在clojure.org/refs上读到
All reads of Refs will see a consistent snapshot of the 'Ref world' as of the starting point of the transaction (its 'read point'). The transaction will see any changes it has made. This is called the in-transaction-value.
在Wikipedia上也有指向"快照隔离"的链接,这意味着一旦事务开始,任何数量的ref的读取将彼此一致。
我做了一个测试用例...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | (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) |
输出为:
1 2 3 4 5 6 7 8 9 10 | S start 1 start 2 start 3 start 1 end 2 end 3 end r1 = 3 S end nil |
请注意,我了解
此行为与上述参考文档如何匹配?
事实证明,如果ref具有一些历史记录,它的行为将与我预期的一样,因此更改ref声明以添加
1 2 3 4 5 6 | (def r1 (ref 0 :min-history 5)) (def r2 (ref 0 :min-history 5)) (dosync (ref-set r1 0) (ref-set r2 0)) |
然后输出为:
1 2 3 4 5 6 7 8 9 10 | S start 1 start 1 end 2 start 2 end 3 start 3 end S r1= 0 S end nil |
在这里阅读,很清楚发生了什么事。读取事务正在重新启动,因为从事务开始之前的引用历史记录中没有任何条目。为了确认,我添加了更多日志记录:
1 2 3 4 5 6 7 8 9 10 | (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"))))) |
不带历史记录mod的输出:
1 2 3 4 5 6 7 8 9 10 11 | S start transaction starting 1 start 2 start 3 start 1 end 2 end 3 end transaction starting S r1= 3 S end |
并带有历史记录mods:
1 2 3 4 5 6 7 8 9 10 11 | S start transaction starting 1 start 2 start 3 start 1 end 2 end 3 end S r1= 0 S end nil |
更新:事实证明,由于测试用例的人为性质,我的上述回答令人分心。在现实世界中,事务是否重新启动无关紧要,因为事务必须以可重新启动的方式编写。运行时不保证在存在/不存在历史记录时只读事务是否将完成。相反,它可以做任何必要的事情来完成交易的世界,并且必须牢记这一点来编写交易代码。这里更详细的讨论
我将上面的内容留作参考。