У меня есть кусок кода в clojure, который должен работать изолированно.Допустим, эта функция
(defn isolate [string1])
Легко изолировать всю функцию на всех входах, вызывая ее так:
(def o (Object. ))
(locking o (isolate string1))
Однако это позволяет только один процесс / поток для одновременного доступа к изоляту.
Теперь я реализовал следующее:
(def current-locks (ref {}))
(defn mergeReverse [x y] (merge y x))
(defn merge-with-current-locks [key val]
(dosync (alter current-locks mergeReverse {key val})))
(defn remove-lock [key]
(dosync (alter current-locks dissoc key)))
и, наконец, блок потоков, вызывающий этот метод
(defn block-until-free [key val]
(let [_ (merge-with-current-locks key val)]
(if (dosync (and (contains? current-locks key)
(not= (get current-locks key) val)))
(do
(Thread/sleep 10)
(block-until-free key val)))))
Как видно из решения, которое я использовалключи и значения здесь, и хотя я блокирую только ключи, но возможность использовать карты вместо массивов была полезной, поскольку я использовал свойство слияния, которое сливается в карту, только если карта не содержит это значение, и поскольку current-locks
являетсяref
Я использовал alter
и поменял входы слияния для получения необходимого поведения.
Этот хак работает, насколько я могу судить (и я его протестировал).Но мой вопрос заключается в том, как я могу сделать это правильным образом?Это решение кажется сложным
Конечно, remove-lock
должен вызываться после выполнения функции критической .