如何保护可变对象免受 Clojure 中的并发 read/write 访问?

How to guard a mutable object against concurrent read/write access in Clojure?

我正在使用 jgrapht,一个用于 java 的图形库作为我的图形操作的 backbone。它会在每次更改时改变其状态,例如添加或删除边或顶点。

我正在从多个线程/go-loops 访问此 "object"。

我已经开始天真地用原子包装图形对象,但据我所知,它不能防止(也不能)防止直接更改其内容的状态。仅当您能够使用 reset!swap! 函数时,它才是安全措施。

我已经更改为 refs 并开始在 dosync 块中进行突变,但我仍然不时注意到一些奇怪的行为。由于它们出现在运行时,因此很难查明它们。如您所料。

我在 Clojure 生态系统方面没有经验,所以如果你能指出一些替代策略来处理 Clojure Java 中的有状态对象,我将不胜感激。

PS:我知道 loom 并愿意使用它,但它缺少我最重要的要求:在加权定向中找到所有简单循环负权重图

据我所知,Clojure 的 STM 功能(例如 dosync、ref)仅与 clojure 的其他功能交互。它们不会以您希望的方式与可变 Java 对象进行交互。原子类型在这里也无济于事,除非您每次想要对其执行操作时都想复制整个图形。不幸的是,在这种情况下,您有一个具有不确定线程​​安全特性的可变对象。你将不得不使用某种锁。有一个获取监视器锁的内置函数:(locking obj body)。您可以只使用图形本身作为监视器对象。或者您可以根据需要创建其他类型的锁,例如读写锁。每次访问图表的一部分或 mutate/update 时,您都需要 acquire/release 锁。

据我了解,您说的是 Java class,而不是 Clojure 数据结构。如果我是对的,那么将该实例包装到原子或任何其他引用类型中是没有意义的,因为您的其余代码仍可能直接修改该实例。

Clojure 有一个特殊的 locking 宏,它在任何 Java 对象上放置一个监视器,同时对其执行一组操作。

它的典型用法可能是这样的:

(def graph (some.java.Graph. 1 2 3 4))
(locking graph
  (.modify graph)
  (.update graph))

有关详细信息,请参阅 documentation page

请注意,我们最近在 JGraphT 中收到了 AsSynchronizedGraph 的贡献:

https://github.com/jgrapht/jgrapht/blob/master/jgrapht-core/src/main/java/org/jgrapht/graph/concurrent/AsSynchronizedGraph.java

它允许您使用使用 ReadWriteLock 的包装器来保护图形。 Javadoc 中有很多使用注意事项。

它尚未发布,但在最新的 SNAPSHOT 版本中可用。