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密集任務

相關文章
相關標籤/搜索