Как защитить изменяемый объект от одновременного доступа на чтение / запись в 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:
Это позволяет защитить граф с помощью оболочки, которая использует ReadWriteLock. В Javadoc есть множество предостережений об использовании.
Он еще не выпущен, но доступен в последней сборке SNAPSHOT.