Как защитить изменяемый объект от одновременного доступа на чтение / запись в Clojure?

Я использую jgraphtбиблиотека графов для Java в качестве основы моих графовых операций. Он изменяет свое состояние при каждом изменении, например, добавляя или удаляя ребро или вершину.

Я получаю доступ к этому "объекту" из нескольких потоков / циклов.

Я начал наивно оборачивать объект графа атомом, но, насколько я понимаю, он не защищает (и не может этого делать) от прямого изменения состояния его содержимого. Это гарантия, только если вы можете использовать reset! или же swap! функции.

Я изменился на refи начал делать мои мутации в dosync блоки, но я все еще замечаю некоторое странное поведение время от времени. Их трудно точно определить, так как они появляются во время выполнения. Как и следовало ожидать.

У меня нет опыта работы с экосистемой Clojure, поэтому я был бы признателен, если бы вы указали мне пару альтернативных стратегий для работы с объектами с состоянием из Java в Clojure.

PS: я в курсе loom и хотел бы использовать его, но в нем нет моего самого важного требования: найти все простые циклы в взвешенном ориентированном графе с отрицательными весами.

3 ответа

Только функции STM Clojure (например, dosync, ref), насколько я понимаю, взаимодействуют с другими функциями clojure. Они не будут взаимодействовать с изменчивыми объектами Java так, как вы можете надеяться. Тип атома здесь тоже не поможет, если только вы не захотите копировать весь график каждый раз, когда хотите выполнить над ним операцию. К сожалению, в этом случае у вас есть изменчивый объект, который имеет неопределенные характеристики безопасности потока. Тебе придется использовать какой-то замок. Есть встроенная функция для захвата блокировки монитора: (блокировка тела объекта). Вы можете просто использовать сам график в качестве объекта монитора. Или вы можете создавать другие виды блокировок, такие как readwrite, как при необходимости. Каждый раз, когда вы получаете доступ к части графика или изменяете / обновляете ее, вам необходимо получить / снять блокировку.

Насколько я понимаю, вы говорите о классе Java, а не о структуре данных Clojure. Если я прав, нет смысла оборачивать этот экземпляр в атом или любой другой ссылочный тип, потому что остальная часть вашего кода все равно может изменить этот экземпляр напрямую.

Clojure имеет специальные locking макрос, который помещает монитор в любой объект Java, выполняя над ним набор действий.

Его типичное использование может быть что-то вроде этого:

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

Смотрите страницу документации для получения дополнительной информации.

Просто обратите внимание, что мы недавно получили вклад AsSynchronizedGraph в JGraphT:

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

Это позволяет защитить граф с помощью оболочки, которая использует ReadWriteLock. В Javadoc есть множество предостережений об использовании.

Он еще не выпущен, но доступен в последней сборке SNAPSHOT.

Другие вопросы по тегам