生成和管理后台线程的惯用 Clojure 方式
Posted
技术标签:
【中文标题】生成和管理后台线程的惯用 Clojure 方式【英文标题】:Idiomatic Clojure way to spawn and manage background threads 【发布时间】:2011-07-14 13:38:51 【问题描述】:什么是惯用的 Clojure 方法来创建一个在后台循环执行更新某些共享 ref 并管理其生命周期的线程?我发现自己为此使用future
,但感觉有点像黑客,因为我从不返回有意义的值。例如:
(future (loop [] (do
(Thread/sleep 100)
(dosync (...))
(recur))))
另外,当不再需要后台处理时,我需要小心future-cancel
这个。关于如何在 Clojure/Swing 应用程序中编排它的任何提示都会很好。例如。添加到我的 UI 中的虚拟 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
))
将是一种可行的方法。
【讨论】:
调用future-cancel
或设置“请取消”标志确实是同样的事情。我仍然需要确保它在正确的时间发生并且稳健地发生。 (我想念RAII)
您关于do
和while
的其他观点当然是正确的。 :)
我不认为我说你应该设置一个请取消标志。如果您的后台任务将在例如一千次迭代之后完成,或者当窗口关闭时,future 可以跟踪这些事情并自行关闭。有些事情比较复杂,需要更细致的管理,对于那些你不妨使用future-cancel。【参考方案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 在蚁群示例应用程序中执行重复任务的方式:Clojure Concurrency
【讨论】:
这是一个非常糟糕的主意。蚁群是preclojure 1.0。他这样做只是因为那里没有未来。 为什么你认为这是个坏主意?以上是关于生成和管理后台线程的惯用 Clojure 方式的主要内容,如果未能解决你的问题,请参考以下文章