Идиоматический способ Clojure для создания и управления фоновыми потоками
Что такое idiomatic Clojure способ создания потока, который петли в фоновом режиме делает обновления для некоторых общих ссылок и для управления его жизненным циклом? Я нахожу себя использующим future
для этого, но он чувствует себя немного взломанным, так как я никогда не возвращаю значимого значения. Например:.
(future (loop [] (do
(Thread/sleep 100)
(dosync (...))
(recur))))
Кроме того, мне нужно быть осторожным с future-cancel
, когда фоновая обработка больше не нужна. Любые советы о том, как организовать это в приложении Clojure/Swing, будут приятными. Например. фиктивный JComponent
, который добавляется в мой пользовательский интерфейс, который отвечает за уничтожение потока при закрытии окна, может быть идеей.
Ответы
Ответ 1
Вам не нужен do
в вашем цикле; это подразумевалось. Кроме того, в то время как нет ничего плохого в безусловном цикле-повторе, вы можете также использовать (while true...).
future
- прекрасный инструмент для этого; не позволяйте этому беспокоить вас, что вы никогда не получите обратно. Это должно вас действительно беспокоить, если вы используете агента, а не будущее, хотя агенты без ценностей являются безумием.
Однако кто сказал, что вам нужно future-cancel
? Просто сделайте один из шагов в будущем, чтобы проверить, все еще нужно. Тогда никакие другие части вашего кода не должны отслеживать фьючерсы и решать, когда их отменить. Так что что-то вроде
(future (loop []
(Thread/sleep 100)
(when (dosync
(alter some-value some-function))
(recur)) ; quit if alter returns nil
))
будет жизнеспособным подходом.
Ответ 2
Использование агентов для фоновых повторяющихся задач кажется мне более аккуратным
(def my-ref (ref 0))
(def my-agent (agent nil))
(defn my-background-task [x]
(do
(send-off *agent* my-background-task)
(println (str "Before " @my-ref))
(dosync (alter my-ref inc))
(println "After " @my-ref)
(Thread/sleep 1000)))
Теперь вам нужно только запустить цикл
(send-off my-agent my-background-task)
Функция my-backgound-task
отправляет себя вызывающему агенту после его выполнения.
Вот как Rich Hickey выполняет повторяющиеся задачи в примере приложения колонии ant: Clojure Concurrency