引用类型Ref #
一、Ref简介 #
Ref是Clojure的软件事务内存(STM)系统核心,用于管理多个需要协调变更的状态。Ref提供ACI事务特性。
1.1 特点 #
- 协调式状态管理
- 软件事务内存(STM)
- ACI特性(原子性、一致性、隔离性)
- 适合多变量协调
1.2 创建Ref #
clojure
(def account-a (ref 1000))
(def account-b (ref 500))
@account-a
(deref account-b)
二、事务基础 #
2.1 dosync #
所有Ref操作必须在事务中进行:
clojure
(def account-a (ref 1000))
(def account-b (ref 500))
(dosync
(alter account-a - 100)
(alter account-b + 100))
[@account-a @account-b]
2.2 alter #
基于当前值更新:
clojure
(def counter (ref 0))
(dosync
(alter counter inc))
@counter
(dosync
(alter counter + 10))
@counter
2.3 ref-set #
直接设置新值:
clojure
(def state (ref :idle))
(dosync
(ref-set state :running))
@state
2.4 commute #
用于可交换的操作,性能更好:
clojure
(def counter (ref 0))
(dosync
(commute counter inc))
@counter
(dosync
(commute counter + 5))
@counter
三、转账示例 #
3.1 基本转账 #
clojure
(def account-a (ref 1000))
(def account-b (ref 500))
(defn transfer [from to amount]
(dosync
(alter from - amount)
(alter to + amount)))
(transfer account-a account-b 200)
[@account-a @account-b]
3.2 带验证的转账 #
clojure
(defn transfer-safe [from to amount]
(dosync
(let [balance @from]
(if (>= balance amount)
(do
(alter from - amount)
(alter to + amount)
{:success true})
{:success false :error "Insufficient funds"}))))
(transfer-safe account-a account-b 100)
(transfer-safe account-a account-b 10000)
3.3 多账户操作 #
clojure
(def accounts
{:a (ref 1000)
:b (ref 500)
:c (ref 200)})
(defn multi-transfer [transfers]
(dosync
(doseq [[from to amount] transfers]
(alter (accounts from) - amount)
(alter (accounts to) + amount))))
(multi-transfer [[:a :b 100] [:b :c 50]])
(map (fn [[k v]] [k @v]) accounts)
四、事务特性 #
4.1 原子性 #
事务要么全部成功,要么全部回滚:
clojure
(def x (ref 0))
(def y (ref 0))
(try
(dosync
(alter x inc)
(throw (ex-info "Error!" {}))
(alter y inc))
(catch Exception e
(println "Caught:" (.getMessage e))))
[@x @y]
4.2 一致性 #
事务中的读取看到一致的状态:
clojure
(def a (ref 0))
(def b (ref 0))
(defn consistent-read []
(dosync
[@a @b]))
(defn update-both []
(dosync
(alter a inc)
(alter b inc)))
(future
(dotimes [_ 1000]
(update-both)))
(Thread/sleep 100)
(consistent-read)
4.3 隔离性 #
并发事务相互隔离:
clojure
(def counter (ref 0))
(defn increment []
(dosync
(let [current @counter]
(Thread/sleep 10)
(ref-set counter (inc current)))))
(def futures
(doall
(for [_ (range 10)]
(future (increment)))))
(doseq [f futures] @f)
@counter
五、事务函数 #
5.1 ensure #
防止其他事务修改:
clojure
(def a (ref 0))
(def b (ref 0))
(defn update-with-ensure []
(dosync
(ensure a)
(alter b + @a)))
(defn update-a []
(dosync
(alter a inc)))
5.2 io! #
标记IO操作,禁止在事务中执行:
clojure
(defn do-io []
(io!
(println "This is IO")))
(def state (ref 0))
(try
(dosync
(do-io)
(alter state inc))
(catch Exception e
(println "Caught:" (.getMessage e))))
六、嵌套事务 #
6.1 嵌套dosync #
clojure
(def x (ref 0))
(defn inner-transaction []
(dosync
(alter x + 10)))
(defn outer-transaction []
(dosync
(alter x inc)
(inner-transaction)))
(outer-transaction)
@x
嵌套事务合并为一个事务。
6.2 事务可见性 #
clojure
(def a (ref 0))
(defn nested-update []
(dosync
(alter a inc)
(dosync
(alter a inc))))
(nested-update)
@a
七、实践示例 #
7.1 银行系统 #
clojure
(defrecord Account [id owner balance])
(defn make-account [id owner balance]
(ref (->Account id owner balance)))
(defn get-balance [account]
(:balance @account))
(defn deposit [account amount]
(dosync
(alter account update :balance + amount)))
(defn withdraw [account amount]
(dosync
(let [balance (:balance @account)]
(if (>= balance amount)
(do
(alter account update :balance - amount)
{:success true})
{:success false :error "Insufficient funds"}))))
(defn transfer-funds [from to amount]
(dosync
(let [result (withdraw from amount)]
(if (:success result)
(do
(deposit to amount)
{:success true})
result))))
(def acc1 (make-account 1 "Alice" 1000))
(def acc2 (make-account 2 "Bob" 500))
(transfer-funds acc1 acc2 200)
[(get-balance acc1) (get-balance acc2)]
7.2 库存管理 #
clojure
(def inventory (ref {:apple 100 :banana 50 :orange 75}))
(defn check-stock [item]
(get @inventory item 0))
(defn reserve [item quantity]
(dosync
(let [current (get @inventory item 0)]
(if (>= current quantity)
(do
(alter inventory update item - quantity)
{:success true :reserved quantity})
{:success false :available current}))))
(defn restock [item quantity]
(dosync
(alter inventory update item (fnil + 0) quantity)))
(reserve :apple 30)
(check-stock :apple)
(restock :banana 20)
(check-stock :banana)
7.3 游戏状态 #
clojure
(def game-state
(ref {:players {}
:enemies []
:items []}))
(defn add-player [id name]
(dosync
(alter game-state
assoc-in [:players id]
{:name name :health 100 :position [0 0]})))
(defn move-player [id new-pos]
(dosync
(alter game-state
assoc-in [:players id :position] new-pos)))
(defn damage-player [id amount]
(dosync
(alter game-state
update-in [:players id :health]
(fn [h] (max 0 (- h amount))))))
(add-player 1 "Alice")
(move-player 1 [10 20])
(damage-player 1 30)
@game-state
八、性能考虑 #
8.1 事务大小 #
clojure
(defn small-transaction []
(dosync
(alter some-ref inc)))
(defn large-transaction []
(dosync
(doseq [r many-refs]
(alter r inc))))
保持事务尽可能小。
8.2 alter vs commute #
clojure
(def counter (ref 0))
(dosync (alter counter inc))
(dosync (commute counter inc))
commute 在低冲突时性能更好,但语义不同。
8.3 避免IO #
clojure
(defn bad-transaction []
(dosync
(let [data @some-ref]
(write-to-db data)
(alter other-ref inc))))
IO操作不应在事务中执行。
九、Ref vs Atom #
| 特性 | Ref | Atom |
|---|---|---|
| 协调 | 多变量 | 单变量 |
| 事务 | 支持 | 不支持 |
| 性能 | 较低 | 较高 |
| 复杂度 | 高 | 低 |
十、总结 #
Ref操作速查:
| 操作 | 说明 |
|---|---|
ref |
创建Ref |
dosync |
创建事务 |
alter |
更新值 |
ref-set |
设置值 |
commute |
可交换更新 |
ensure |
保护读取 |
关键点:
- Ref用于协调多变量状态
- 所有操作必须在
dosync中 - STM提供ACI特性
- 避免在事务中执行IO
- 保持事务小而快
下一步,让我们学习代理类型Agent!
最后更新:2026-03-27