读书人

Clojure-JVM下的函数式编程语言(11)引

发布时间: 2012-09-03 09:48:39 作者: rapoo

Clojure-JVM上的函数式编程语言(11)引用类型 作者: R. Mark Volkmann

?原帖地址:http://java.ociweb.com/mark/clojure/article.html#ReferenceTypes

?作者:R. Mark Volkmann

?译者:RoySong

?

引用类型(Reference types)

????引用类型是针对不可变数据的可变引用。在Clojure中有四种引用类型: Vars, Refs, Atoms和Agents。

它们有很多相同点。

它们都能够包含任意类型的对象。它们都能够被间接引用,在采取deref函数或者@读取器宏来检索它们中包含的对象时。它们都支持验证器(validators)-- 当值被改变时调用的函数,如果新的值是合法有效的,验证器就会返回true。否则验证器会返回false或者抛出一个异常。如果验证器仅仅返回false,则包含"Invalid reference state"信息的IllegalStateException将被抛出。它们都支持监视器(watchers)Agents。当一个被监视的值引用改变时,对应的Agent就被唤醒。更多地细节,参见"Agents"一节。

??? 下面的表格简略介绍了四种引用类型的区别,以及能够创建和修改它们的函数。下面表格中的每个函数将在接下来进行

讨论。

Var Ref Atom Agent 用途同步改变到一个单一的、线程本地(thread-local)的值
同步、调整改变到一个或者多个值同步改变到一个单一的值同步改变到一个单一的值
创建 (def name initial-value) (ref initial-value) (atom initial-value) (agent initial-value) 修改 (def name new-value)
sets new root value (alter-var-root
(var name) update-fn args)

atomically sets new root value (set! name new-value)
sets new, thread-local value inside a binding form (ref-set ref new-value)
must be inside a dosync
(alter ref
update-fn arguments)

must be inside a dosync
(commute ref
update-fn arguments)

must be inside a dosync (reset! atom new-value)
(compare-and-set! atom current-value new-value)
(swap! atom
update-fn arguments)
(send agent
update-fn arguments)

(send-off agent
update-fn arguments)

?

Vars

??? Vars是一个引用,它能够拥有一个所有线程共享的根绑定(root binding),也能够在每个线程中拥有不同的值

(thread-local)。

?

??? 创建Var并设置根绑定:

?

????接下来的函数支持从一个账户往另一个账户转账。线程通过dosync开始以保证存取动作都发生或者都不发生。

?

??? 下面的函数支持汇报当前账户的状态。线程通过dosync开始以保证账户汇报的一致性。比如说,它不会汇报

转账中的余额。

?

??? 上述代码并未处理已开始线程中产生的异常。做为代替,我们在当前线程中为这些异常定义一个异常处理器。

?Atoms

??? Atoms提供了一种比联合Ref和STM更简单的机制来更新单一值。它们不会受到线程的影响。

?

????有三个函数能够改变Atom的值:reset!, compare-and-set!和swap!。

?

??? reset!函数能够直接重设Atom的值为新值,而不管原来的值是什么。例子如下:

?

????为什么这段代码的输出是3?update-atom函数在reset!函数之前被另一个线程调用了,它获取到Atom的当前值1.

然后它进入休眠让reset!函数有时间去执行。在那之后,Atom的值为3.当update-atom函数调用compare-and-set!来

让Atom的值增加时,会失败因为Atom的当前值已经不是原本的1了。这代表着Atom当前的保持值为3.

?

??? swap!函数的第一个参数是目标Atom,然后是一个用来计算Atom新值的函数,然后是任意数量的参数用来传给

计算新值函数。计算新值函数被调用时传入的是目标Atom的当前值以及如果存在的任意数量的参数。swap!函数

本质上是 compare-and-set!的一个包装器,除了一个重要的不同。它首先对Atom取值并保存Atom的当前值。然后

它调用函数来计算一个新值。最后它调用compare-and-set!并把第一步保存的值传入。如果compare-and-set!

返回false,说明Atom的值不同于调用函数前的值,然后swap!函数会被重复调用直到检查通过为止。这就是那个

重要的不同。上面的代码采用swap!和compare-and-set!来写的例子如下:

?

??? 为什么输出的结果是4?在reset!函数调用之前,另一个线程调用了swap!函数。当在swap!函数中调用了update-atom

之后,Atom的当前值是1.但是,由于sleep调用,update-atom函数无法调用结束,直到reset!调用结束,将Atom的

值设为3.然后update-atom函数返回2.在swap!函数将Atom的值设为2之前,它会检查Atom的当前值是否仍然是1.它现在

不是,所以swap!函数再次调用update-atom函数。这次Atom的当前值是3,所以它增加了1并返回4.现在swap!函数

验证当前值成功了,并将Atom设值为4.

?

Agents

??? Agents被用来运行相互之间无须协调的分离线程中的任务。它对于改变一个作为某个Agent值的单一对象的状态来说

非常有用。这个值通过在单独线程中运行某个”行为“("action")来改变。这个行为是一个函数,接收Agent的当前值

作为参数,并可以接收其他的一些可选参数。对于指定的Agent,一次只能运行一个行为。

?

????agent函数创建了一个新的Agent,如下:




读书人网 >编程

热点推荐