10

我的 Clojure 任务正在取得进展(在 4clojure.com 上解决了大约 80 个问题),我继续阅读和编码并尝试“得到它”。

现在我对 Clojure 被设计为“无锁并发”感到有点困惑。我对死锁非常了解(例如:“我编写的 Java 代码很糟糕,最终陷入死锁”,而不是“我是并发专家”)。我也读过这个:

为什么无锁并发如此重要(在 Clojure 中)?

我意识到 Clojure 程序不能死锁是多么的棒。

但我有点困惑:这样的壮举是通过在后台实现无锁算法实现的,还是使用了潜在的“可死锁”算法,但使用正确的实现保证永远不会死锁(这会以某种方式对 Clojure 程序员“隐藏” )?

最近有一个关于无锁算法的黑客新闻讨论:

http://news.ycombinator.com/item?id=4103921

参考1024cores.net上的以下“无锁算法”页面:

http://www.1024cores.net/home/lock-free-algorithms

我不明白这篇文章和 Clojure 下并发如何工作之间的关系。

这让我完全困惑:当我在 Clojure 中开发并发程序时,这是否意味着“锁和无锁算法”对我来说不是问题?

4

2 回答 2

9

一般来说,Clojure 通过正确处理时间来避免锁定问题在许多系统中,对象的时间是一个非常松散的概念,因为时间 1(更新前)的对象被编辑到时间 2(更新后)成为该对象),在该过程中它既不是第一也不是第二,因此我们使用锁来确保它仅在此转换之前或之后可见。协调锁由此而来,而死锁则来自那......

它是算法、数据结构和时间的组合。

Clojure 通过结合不可变数据结构、函数式编程和协调时间模型(参考、原子、代理等)来做到这一点。在这个模型中,一个函数获取一些东西并产生它的下一个版本,同时保留过去,只要有人在看它(直到 GC 得到它)

  • 不可变数据结构:Clojure 的集合在 FP 的意义上是持久的。制作新版本后,旧副本“仍然存在”。这样观察者就不需要锁定对象,因为它们永远不会从它们下面变出来。可能存在基于他们正在查看的版本的较新版本,尽管没有任何内容会改变他们的副本。

  • 函数式编程:纯粹(或尽可能接近没有区别)函数在一个时间点获取一个集合并生成下一个版本,而不共享它们的内部状态,因此不需要锁定。这还有许多其他好处。

  • 协调时间:当确实需要协调多个对象时,就像任何有趣的系统一样,那么 Clojure 的时间模型就会发挥作用。不同的目的有不同的机制。这有一个内部用于计算时间增量的锁,因此恰好有一个时间零,一个时间一和一个时间 N。所以它不是严格无锁的。STM 包含您永远*不需要与之交互的锁


*好吧...几乎从来没有;-)

于 2012-06-14T20:25:02.063 回答
4

如果您搜索 Clojure 源代码,特别是 .java 文件,您会发现很多对该java.util.concurrent包的引用。该java.util.concurrent软件包是纽约州立大学奥斯威戈分校的 Doug Lea 数十年来对并发性研究的结晶。具体来说,存在对AtomicReference允许访问“比较和交换”(也称为“比较和设置”或 CAS)指令的原子变量类(例如)的引用。CAS 指令有点难以解释(我在下面提供了参考),但正确使用 CAS 是算法“无锁”(至少在 Java 世界中)意味着什么的核心。无锁算法最终会为高并发应用程序带来更高的吞吐量和更少的争用;正是 Clojure 所针对的域。

要深入了解这个主题,请阅读Brian Goetz 的Java Concurrency in Practice。另请参阅同一作者的这篇文章。

作为旁注,我总是发现很难java.util.concurrent直接使用这个包,即使它已经进化了。对我来说只是感觉太低级了。Clojure 的美妙之处在于它提供了对专家并发库的访问,但通过非常易于使用的软件事务内存 (STM) 抽象来实现。这真的是一个很大的成就。

于 2012-06-15T06:10:46.300 回答