标签:
在Clojure中,几乎任何事物都是一个值(value)。
状态(State)就是一个identity在某个时间点的值。
对于identity,在Clojure中提供了四种引用类型。
;Clojure中大部分对象都是不可变的。如果需要可变数据,如下定义一个引用。
(def current-track (ref "Mars, the Bringer of War"))
-> #‘user/current-track
;解引用
(deref current-track)
-> "Mars, the Bringer of War"
@current-track
-> "Mars, the Bringer of War"
;给引用设置新的值(ref-set)
;由于引用可变,因而需要在dosync开启的事务内进行。
(dosync (ref-set current-track "Venus, the Bringer of Peace"))
;正如数据库事务一样,STM事务有三个性质。
;原子性(atomic)
;一致性(consistent),引用类型可以指明校验函数,任何一个校验失败,事务失败。
;隔离性(isolated),事务之间不可见。
;然而数据库事务还有一个特性,可持久性(durable)。由于STM是内存中事务,无法保证持久性。事务的四个性质简称为ACID,STM只提供了ACI。
;在同一个事务中发生的变更,称之为coordinated,即在外界看来这些变更同时发生。
(dosync
(ref-set current-track "Credo")
(ref-set current-composer "Byrd"))
-> "Byrd"
;更新引用,使用alter可以接受一个变更函数,函数的参数包含引用当前值,返回值即为引用的新值。
(defrecord Message [sender text])
(def messages (ref ()))
(defn add-message [msg]
(dosync (alter messages conj msg)))
STM使用的技术称之为Multiversion Concurrency Control
简单说,事务A在开始时获取一个代表时间戳的整数(point),以及所有涉及到的引用值的一份私有拷贝(effectively private copy)。所有的操作针对这份私有拷贝进行,在进行的过程中,如果有任何一个其他的事务企图对A所涉及的引用进行set/alter,那么A会强制重新开始。如果在dosync中抛出了异常,那么事务结束,不再重试。
有时你觉得alter
这种过分的谨慎并无必要,可以使用性能更好的commute
commute是alter的一个特殊变种,它允许更多的并发。当然正如函数名所表明的,对于commute发起的变更之间的顺序是可变的,即STM可以有重排列的自由。当前事务并不会因为其他事务更改了引用而重启。理论上聊天记录的更新不应该使用commute,然而实际中STM对commute的重排发生在微妙级别,所以不会有影响。
很多数据更新之间的顺序是不可交换的(not commutative)。例如一个产生唯一ID的计数器。
(def counter (ref 0))
(defn next-counter [] (dosync (alter counter inc)))
其他情况也尽量使用alter而不是commute,因为alter不容易出错,最大的问题只是效率可能低一点而已。
数据库事务为了保证一致性,会进行很多完整性检查。STM也可以通过给ref添加校验函数进行类似的检查。
(def validate-message-list
(partial every? #(and (:sender %) (:text %))))
(def messages (ref () :validator validate-message-list))
;试图添加违法数据会发生异常
(add-message "not a valid message")
-> java.lang.IllegalStateException: Invalid reference state
相较于由一个事务进行协调(coordinated)ref更新,Atoms是一种更加轻量的机制。用于非协调的对单个值进行更新。
;定义一个atom
(def current-track (atom "Venus, the Bringer of Peace"))
-> #‘user/current-track
;解引用atom
(deref current-track)
-> "Venus, the Bringer of Peace"
@current-track
-> "Venus, the Bringer of Peace"
;设置新值
(reset! current-track "Credo")
-> "Credo"
;使用atom指向一个map
(def current-track (atom {:title "Credo" :composer "Byrd"}))
-> #‘user/current-track
;重新设置map
(reset! current-track {:title "Spem in Alium" :composer "Tallis"})
-> {:title "Spem in Alium", :composer "Tallis"}
;仅仅设置map的一个字段,使用swap!函数
(swap! current-track assoc :title "Sancte Deus")
-> {:title "Sancte Deus", :composer "Tallis"}
;定义一个Agent
(def counter (agent 0))
-> #‘user/counter
;通知Agent更新数据,注意是异步的
(send counter inc)
-> #<clojure.lang.Agent@23451c74: 0>
标签:
原文地址:http://blog.csdn.net/code_for_fun/article/details/51331007