Programming Clojure学习笔记——并发

来源:互联网 发布:win10禁止安装软件 编辑:程序博客网 时间:2024/04/30 11:57
6.2 引用和软件事务存贮
在Clojure中,大部分对象都是不可变的。当你真的需要可变数据时,你必须明确说明,如创建一个可变的引用指向可变对象:
(ref initial-state)
如:(def current-track (ref "Mars, the Bringer of War")) 定义引用current-track

ref包装并保护它内部的状态,控制对它内部状态的访问。可以使用deref查看引用内容:
(deref reference)
deref函数可以缩写为读者宏@。如:
user=> (deref current-track)
"Mars, the Bringer of War"
user=> @current-track
"Mars, the Bringer of War"

ref-set
使用ref-set修改引用指向:
(ref-set reference new-value)
因为引用refs是可变的,必须保护对它们的修改;在Clojure中,使用事务来保护,事务通过dosync进行包装:
(dosync & exprs)

因此修改current-track指向另一首歌曲,可以写成:
(dosync (ref-set current-track "Venus, the Bringer of Peace"))

事务的特性:
类似数据库事务,STM事务满足以下一些特性:
(1) 更新是原子的
在一个事务中更新多个引用refs,对外界来说都是一起完成的,而不是一个一个完成的。
(2) 更新是一致的
如果事务中有一个更新失败,则事务中所有更新都将失败
(3) 更新是隔离的
其他事务看不到运行事务中部分更新的结果
由于Clojure的事务在内存中执行结果不保存到物理存储上,所以不具有持久化特性。

alter
在事务中,修改引用执行的对象
(alter ref update-fn & args...)

举例说明:
定义一个消息结构
(defstruct message :sender :text)
定义一个存放消息的列表,初始为空:
(def messages (ref ()))
向列表中添加消息则可以写成:
(defn add-message [msg] (dosync (alter messages conj msg)))
函数add-message向列表message中添加消息,如:
user=> (add-message (struct message "user 1" "hello"))
({:sender "user 1", :text "hello"})

STM如何工作:MVCC
Clojure的STM使用一种在多个主要数据库中使用的称为Multiversion Concurrency Control(MVCC)的技术。在Clojure中,MVCC是如下工作的:
事务A开始时,获取一个类似唯一时戳的数字。然后获取需要更新的引用的一个有效拷贝,这些拷贝与那个类似时戳的数字关联。
如果在任何点,STM检测到其他事务已经修改了事务A要修改的引用,则事务A被强制重试。如果在dosync块抛出异常,则事务A终止不再重试。
如果并当事务A提交后,它所做的修改将与事务时间线的一个点关联暴露给外部。

commute
commute是alter的一个变体,允许更多的并发:
(commute ref update-fn & args...)
commute所执行的修改与顺序无关,即STM可以自由调整commute中修改操作的顺序。
如果其他事务在commute中修改commute将要修改的引用ref,则STM不会重新执行整个事务,而只是重新执行该修改操作。

偏好alter
尽量少用commute,除非你明确知道事务中的各操作与顺序无关。

给引用Refs添加验证
创建引用时,可以指定一个验证函数:
(ref initial-state options*)
这里选项options包括
(1) :validator validate-fn
(2) :meta metadata-map
验证函数可以抛出异常阻止事务完成
示例:
定义验证函数
(def validate-message-list
   (partial every? #(and (:sender %) (:text %))))
给引用指定验证函数
(def messages (ref () :validator validate-message-list))
user=> (add-message "not a valid message")
  java.lang.IllegalStateException: Invalid reference state
数据格式不对抛出异常
原创粉丝点击