Clojure的引用类型:var,ref,agent和atom

做为其余值的容器,也均可以解引用。函数

deref不会阻塞。atom

add-watch

(add-watch reference key fn)

能够定义引用值发生改变时的回调,fn是4个参数 :key (atom/var/agent) 旧状态 新状态spa

(def a (atom {}))

(add-watch a :watcher (fn [key atom old-state new-state] (prn "-- Atom Changed --") (prn "key" key) (prn "atom" atom) (prn "old-state" old-state) (prn "new-state" new-state))) (reset! a {:foo "bar"}) ;; "-- Atom Changed --" ;; "key" :watcher ;; "atom" #<Atom@4b020acf: {:foo "bar"}> ;; "old-state" {} ;; "new-state" {:foo "bar"} ;; {:foo "bar"}

watcher在每次修改时都会被调用,可是不保证真的有改变。所以可能须要本身比较新旧值 线程

key用于给1个ref上添加多个不一样watcher,移除watcher也要用keycode

(remove-watch reference key)

 

atom

用swap! 修改blog

;; make an atomic list
(def players (atom ()))
;; #'user/players

;; conjoin a keyword into that list
(swap! players conj :player1)
;;=> (:player1)

;; conjoin a second keyword into the list
(swap! players conj :player2)
;;=> (:player2 :player1)

;; take a look at what is in the list
(deref players)
;;=> (:player2 :player1)

 

swap! 接受函数 和参数,把atom里的做为第1个参数。后面参数不限队列

 若是f的过程复杂,好比comp 几个函数计算时间会长, 在这期间atom的值可能被修改,而若是使用atom的老值计算,可是返回前atom改为了新值,此时,老值的计算结果会被放弃。事务

能够强行reset!内存

ref与STM

软件事务内存用于避免手工管理锁;作用域

垃圾回收用于避免手工管理内存。

——自动化的手段封装那些领域无关,繁琐的细节

 Clojure 知足了ACI  而D持久不是STM关心的,由于是纯内存。

alter commute ref-set 用于修改

书上例子是

角色:

(defn character
  [name & {:as opts}]
  (ref (merge {:name name :items #{} :health 500} opts))
)

从1个角色身上移动1个装备到另外一个角色身上

(defn loot
  [from to]  
  (dosync
    (when-let [item (first (:items @from))]
      (alter to update-in [:items] conj item)
      (alter from update-in [:items] disj item)
    )
  )
)

dosync表示一个事务

而when-let 若是[]里是nil 则不会执行body

disj从集合里去掉元素,返回剩余的集合

而alter,update-in  @  也都各有意义。

感受读一块Clojure代码简直不是读代码,而是读古文。没有1个字多余,字认不齐,就1个完整句子都读不下来

 

alter: 记录ref在事务内第一个alter调用的值,按乐观方式提交,若是ref的值已经被别人改变,则整个事务失败。重试。

commute

可交换的计算,只关心最终结果

不会发生冲突,也不会重试,提升吞吐量,用ref最新值重算一遍

 

STM的事务若是耗时间太长,可能有活锁问题,老是提交失败(总有更快的任务来插队)。

write-skew 写误差

在读、写之间 deref的其余值发生了改变。这时,把deref改为ensure,保证依赖的值被锁住,其余线程更新不了。

var 

 

用def定义。var实际上是ns级可变引用。不是“变量”

元数据修饰

私有和不可变

(def ^:private  X 42)
(def ^:const  X 42)

使用前声明

(def j)
;= #'user/j
j
;= #<Unbound Unbound: #'user/j>

主要是用于表示函数的占位符 ,好比引用其余位置的的公共API。

还能够直接用宏(declare j)表示这时使用前

动态做用域

(def ^:dynamic *X*)
(binding [*X* 500]
  (...)
)

习惯把这种变量两端加**,护耳。

给了函数一个隐式参数,在函数深层埋入须要改变的配置项。防止层层传递。

但坏处嘛,多少有点破坏封装的意思。

 agent

每一个agent维护1个action队列。

send在固定大小的线程池上执行,用于CPU密集任务

send-off不限制线程池大小,适合IO密集任务

相关文章
相关标签/搜索