Clojure可变存储类型

时间:2009-06-22 16:54:58

标签: multithreading clojure future mutable

我正在尝试从网站上提供的API和文档中学习Clojure。我对Clojure中的可变存储有点不清楚,我想确保我的理解是正确的。如果有任何想法我错了,请告诉我。

编辑:我正在更新此内容,因为我收到有关其正确性的评论。


免责声明:所有这些信息都是非正式的,可能是错误的。不要使用这篇文章来了解Clojure的工作原理。


Vars 始终包含根绑定,可能还包含每个线程的绑定。它们与命令式语言中的常规变量相当,不适合在线程之间共享信息。 (感谢Arthur Ulfeldt)

Refs 是支持原子事务的线程之间共享的位置,这些事务可以更改单个事务中任意数量的ref的状态。在退出同步表达式(dosync)时提交事务,并且使用STM魔法(回滚,队列,等待等)自动解决冲突。

代理是通过调度独立的操作函数来更改代理的状态,使线程之间异步共享信息的开销最小的位置。虽然在调度函数完成之前未设置代理的值,但代理会立即返回并因此是非阻塞的。

Atoms 是可以在线程之间同步共享的位置。它们支持不同线程之间的安全操作。

这是我基于何时使用这些结构的友好总结:

  • Vars就像命令式语言中的常规旧变量。 (尽可能避免)
  • 原子就像Vars,但具有线程共享安全性,可以立即读取和安全设置。 (感谢Martin)
  • 一个代理就像一个Atom,但它不是阻塞它而是产生一个新的线程来计算它的值,只有在改变一个值时才会阻塞,并且可以让其他线程知道它已经完成了分配。
  • Refs是在事务中锁定自己的共享位置。我们只是启动一个事务并让Clojure处理该事务中refs之间的所有锁定条件,而不是让程序员决定在每个锁定代码的竞争条件下会发生什么。

此外,相关概念是函数future。对我来说,似乎未来的对象可以被描述为同步代理,在完成计算之前根本无法访问该值。它也可以描述为非阻塞Atom。这些准确的未来概念是什么?

5 个答案:

答案 0 :(得分:5)

听起来你真的得到了Clojure!干得好:))

Vars在所有线程中都有一个“根绑定”可见,并且每个单独的线程都可以更改它看到的值,而不会影响其他线程。如果我的理解是正确的,var只能在一个线程中不存在,并且所有人都可以看到root绑定,并且在第一次使用(def ...)定义之前它不能“反弹”。

Refs在包含更改的(dosync ...)事务结束时提交,但仅在事务能够以一致状态完成时才提交。

答案 1 :(得分:4)

我认为你对Atoms的结论是错误的:

  

Atoms就像Vars,但是线程共享安全性会阻塞,直到值发生变化

使用swap!更改原子或使用compare-and-set!更改原子。这从不阻止任何事情。 swap!就像只有一个参考的交易一样:

  1. 旧值取自atom并存储thread-local
  2. 该函数应用于旧值以生成新值
  3. 如果成功,则使用旧值和新值调用compare-and-set;只有当原子的值没有被任何其他线程改变(仍然等于旧值)时,才会写入新值,否则操作将在(1)重新开始,直到最终成功。

答案 2 :(得分:3)

我发现你的问题有两个问题。

你说:

  

如果在操作发生时访问代理,则在操作完成之前不会返回该值

http://clojure.org/agents说:

  

代理的状态总是可以立即被任何线程读取

即。你永远不必等待获得代理的价值(我假设一个动作改变的值被代理并原子地改变)。

deref的{​​{1}} - 方法的代码如下所示(SVN修订版1382):

Agent

}

不涉及阻止。

另外,我不明白你的意思(在你的参考部分)

  

在调用deref

时提交事务

当dosync块的所有操作都已完成时,将提交事务,不会抛出任何异常,也没有任何操作导致重试事务。我认为public Object deref() throws Exception{ if(errors != null) { throw new Exception("Agent has errors", (Exception) RT.first(errors)); } return state; 与此无关,但也许我误解了你的观点。

答案 3 :(得分:1)

当他说Atoms操作重新开始时,马丁是对的,直到最终成功。 它也被称为旋转等待。 虽然注意到锁定时确实阻塞了操作的线程被阻塞,直到操作成功,因此它是阻塞操作而不是异步操作。

同样关于期货,Clojure 1.1增加了承诺和期货的抽象。 promise是一种同步构造,可用于将值从一个线程传递到另一个线程。在价值交付之前,任何取消引用承诺的企图都将被阻止。

(def a-promise (promise))
(deliver a-promise :fred)

期货代表异步计算。它们是一种让代码在另一个线程中运行并获得结果的方法。

(def f (future (some-sexp)))
(deref f) ; blocks the thread that derefs f until value is available

答案 4 :(得分:0)

Vars并不总是有根绑定。使用

创建没有绑定的var是合法的
(def x)

(declare x)

尝试在x具有值之前对其进行评估将导致

Var user/x is unbound.
[Thrown class java.lang.IllegalStateException]