码迷,mamicode.com
首页 > 其他好文 > 详细

Programming Clojure笔记之五——状态

时间:2016-05-07 09:35:17      阅读:250      评论:0      收藏:0      [点我收藏+]

标签:

在Clojure中,几乎任何事物都是一个值(value)。
状态(State)就是一个identity在某个时间点的值。
对于identity,在Clojure中提供了四种引用类型。

  • Refs,对共享状态以coordinated,synchronous方式进行更改
  • Atoms, 对共享状态以uncoordinated,synchronous方式进行更改
  • Agents,对共享状态以asynchronous方式进行更改
  • Vars,管理线程局部状态。

Ref和软事务内存(Software Transactional Memory)

;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

;给引用设置新的值(ref-set)
;由于引用可变,因而需要在dosync开启的事务内进行。
(dosync (ref-set current-track "Venus, the Bringer of Peace"))

STM的性质

;正如数据库事务一样,STM事务有三个性质。
;原子性(atomic)
;一致性(consistent),引用类型可以指明校验函数,任何一个校验失败,事务失败。
;隔离性(isolated),事务之间不可见。
;然而数据库事务还有一个特性,可持久性(durable)。由于STM是内存中事务,无法保证持久性。事务的四个性质简称为ACID,STM只提供了ACI。

;在同一个事务中发生的变更,称之为coordinated,即在外界看来这些变更同时发生。
(dosync
  (ref-set current-track "Credo")
  (ref-set current-composer "Byrd"))
-> "Byrd"

alter

;更新引用,使用alter可以接受一个变更函数,函数的参数包含引用当前值,返回值即为引用的新值。
(defrecord Message [sender text])
(def messages (ref ()))
(defn add-message [msg]
(dosync (alter messages conj msg)))

STM的工作原理:MVCC

STM使用的技术称之为Multiversion Concurrency Control

简单说,事务A在开始时获取一个代表时间戳的整数(point),以及所有涉及到的引用值的一份私有拷贝(effectively private copy)。所有的操作针对这份私有拷贝进行,在进行的过程中,如果有任何一个其他的事务企图对A所涉及的引用进行set/alter,那么A会强制重新开始。如果在dosync中抛出了异常,那么事务结束,不再重试。

有时你觉得alter这种过分的谨慎并无必要,可以使用性能更好的commute

commute

commute是alter的一个特殊变种,它允许更多的并发。当然正如函数名所表明的,对于commute发起的变更之间的顺序是可变的,即STM可以有重排列的自由。当前事务并不会因为其他事务更改了引用而重启。理论上聊天记录的更新不应该使用commute,然而实际中STM对commute的重排发生在微妙级别,所以不会有影响。

优先选择使用alter

很多数据更新之间的顺序是不可交换的(not commutative)。例如一个产生唯一ID的计数器。

(def counter (ref 0))
(defn next-counter [] (dosync (alter counter inc)))

其他情况也尽量使用alter而不是commute,因为alter不容易出错,最大的问题只是效率可能低一点而已。

给Ref添加校验

数据库事务为了保证一致性,会进行很多完整性检查。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

使用Atom进行非协调的同步更新

相较于由一个事务进行协调(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进行异步更新

;定义一个Agent
(def counter (agent 0))
-> #‘user/counter

;通知Agent更新数据,注意是异步的
(send counter inc)
-> #<clojure.lang.Agent@23451c74: 0>

Programming Clojure笔记之五——状态

标签:

原文地址:http://blog.csdn.net/code_for_fun/article/details/51331007

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!