我想知道我是否只能在事务中获得一致的读取.以下是一些用于说明问题的代码:
(def foo (ref 0))
(def bar (ref 0))
(defn incer [] (dosync (alter foo inc) (alter bar inc)))
(.start (Thread. (fn [] (last (repeatedly incer))))) ;; create a lot of action
Run Code Online (Sandbox Code Playgroud)
现在我想打印foo和bar的值
(println @foo @bar)
;=> 328498765 328498766
Run Code Online (Sandbox Code Playgroud)
我知道我可以使用ensure获得一致的值
(dosync (ensure foo) (ensure bar) (println @foo @bar))
;=> 356117587 356117587
Run Code Online (Sandbox Code Playgroud)
我想知道这是否是唯一的方法,或者是否有更好的解决方案.在他的演讲中"我们还在吗?" (http://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hickey)at min 55 Rich显示了一张幻灯片,暗示有一些方法可以在不将感知置于事务中的情况下进行,但是我无法弄清楚如何.
您确实需要一个事务来获得一致的读取,但您不需要ensure:
;; will print consistent values
(dosync (println @foo @bar))
Run Code Online (Sandbox Code Playgroud)
事务中的Ref读取操作就好像针对整个系统状态的快照执行一样.如果无法获得此意义上的一致值,则重试该事务.存在一种历史机制,其存储每个Ref的多个先前值 - 在每个Ref基础上可以配置的数字(参见参考资料(doc ref)),默认情况是每个Ref开始时根本不存储任何历史记录当它参与失败的快照场景时,它会添加一个新条目,最多不超过10个.
ensure的目的是防止写入偏斜,同时允许更多的并发性(ref-set some-ref @some-ref).要了解写入倾斜,请参阅快照隔离的Wikipedia页面.作为简短的总结,当两个或多个事务读取重叠的Refs集并写入非重叠的Refs集时,可能会发生异常:
(def foo (ref 0))
(def bar (ref 0))
;; timeouts added to the transaction bodies
;; to make the demonstration reliable:
(future (dosync
(let [f @foo
b @bar]
(Thread/sleep 1000)
(if (zero? (+ f b))
(alter foo dec)))))
(future (dosync
(let [f @foo
b @bar]
(Thread/sleep 1000)
(if (zero? (+ f b))
(alter bar dec)))))
(println @foo @bar)
Run Code Online (Sandbox Code Playgroud)
这是可能的交易双方同时读参考文献要么事务提交之前,并愉快地继续递减两个参考文献的价值观,而明确交易的任何串行执行只会递减一个值.更改@bar到(ensure bar)和@foo到(ensure foo)会排除这种情况.
(实际上(ensure bar)只在不修改的事务中需要,bar并且(ensure foo)只在不修改的事务中需要foo.在ensure有用的场景中,它可以用来代替deref/ @,因为它(ensure foo)返回事务中的值foo.)
| 归档时间: |
|
| 查看次数: |
218 次 |
| 最近记录: |