如何在Clojure中创建一个不断运行的后台进程?

16

我该如何在Clojure中创建一个持续运行的后台进程?使用永不结束的循环和"future"是否是正确的方法?

3个回答

18

你可以通过启动一个永远运行的函数来开启一个线程。

(defn forever []
  ;; do stuff in a loop forever
)

(.start (Thread. forever))

如果您不希望后台线程阻止进程退出,请确保将其设置为守护线程:

(doto 
   (Thread. forever)
   (.setDaemon true)
   (.start))

如果您想要更多的精细控制,可以使用java.util.concurrent.Executors工厂来创建ExecutorService。这样可以轻松创建线程池,使用自定义线程工厂、自定义输入队列等。
如果您想使用更符合Clojure风格的API,claypoole库将一些工作执行内容进行了封装。

7

我简单的高阶无限循环函数(使用 futures):

(def counter (atom 1))

(defn infinite-loop [function]   
  (function)
  (future (infinite-loop function))
  nil) 

;; note the nil above is necessary to avoid overflowing the stack with futures...

(infinite-loop 
  #(do 
     (Thread/sleep 1000) 
     (swap! counter inc)))

;; wait half a minute....

@counter
=> 31

我强烈建议使用原子(atom)或者Clojure的其他引用类型来存储结果(就像上面示例中的计数器一样)。

通过一点调整,您也可以使用这种方法以线程安全的方式启动/停止/暂停进程(例如,在循环的每次迭代中测试一个标志以查看是否应该执行(function))。


2
顺便提一下,这种方法的开销非常小 - 如果删除Thread/sleep,您可以每秒获得超过一百万个计数器增量。 - mikera
另一个简单的实现,不会消耗堆栈(defn infinite [f seconds] (future (loop [] (f) (Thread/sleep (* seconds 1000)) (recur)))) - Jaime Agudo
@James - 我的实现也不会消耗堆栈。递归调用 infinite-loop 的事实是在一个 future 中,这意味着它不会在原始函数的堆栈帧内重复出现。 - mikera

1

它允许您暂停执行吗? - yazzapps.com

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接