Идиоматический способ Clojure для порождения и управления фоновыми потоками - PullRequest
15 голосов
/ 13 марта 2011

Каков идиоматический способ Clojure для создания потока, который зацикливается в фоновом режиме, обновляя некоторые общие ссылки и управляя его временем жизни? Я использую future для этого, но это похоже на хак, так как я никогда не возвращаю значащее значение. E.g.:

(future (loop [] (do
    (Thread/sleep 100)
    (dosync (...))
    (recur))))

Кроме того, мне нужно быть осторожным с future-cancel, когда фоновая обработка больше не нужна. Любые советы о том, как организовать это в приложении Clojure / Swing, были бы хорошими. Например. Идея JComponent, добавленная в мой пользовательский интерфейс и отвечающая за уничтожение потока при закрытии окна, может быть идеей.

Ответы [ 2 ]

9 голосов
/ 13 марта 2011

Вам не нужно do в вашем цикле; это подразумевается. Кроме того, хотя нет ничего плохого в безусловном циклическом повторении, вы также можете использовать (пока true ...).

future - прекрасный инструмент для этого; не позволяйте вам беспокоиться о том, что вы никогда не получите значение обратно. Это должно действительно беспокоить вас, если вы используете агента, а не будущее - агенты без ценностей - это безумие.

Однако, кто сказал, что вам нужно future-cancel? Просто сделайте один из шагов в будущем, чтобы проверить, все ли еще нужно. Тогда никакие другие части вашего кода не должны отслеживать фьючерсы и решать, когда отменить их. Так что-то вроде

(future (loop []
          (Thread/sleep 100)
          (when (dosync
                 (alter some-value some-function))
            (recur)) ; quit if alter returns nil
          ))

было бы жизнеспособным подходом.

0 голосов
/ 14 марта 2011

Использование агентов для фоновых повторяющихся задач мне кажется более подходящим

(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 colony: Clojure Concurrency

...