【问题标题】:Idiomatic Clojure way to spawn and manage background threads生成和管理后台线程的惯用 Clojure 方式
【发布时间】:2011-07-14 13:38:51
【问题描述】:

什么是惯用的 Clojure 方法来创建一个在后台循环执行更新某些共享 ref 并管理其生命周期的线程?我发现自己为此使用future,但感觉有点像黑客,因为我从不返回有意义的值。例如:

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

另外,当不再需要后台处理时,我需要小心future-cancel 这个。关于如何在 Clojure/Swing 应用程序中编排它的任何提示都会很好。例如。添加到我的 UI 中的虚拟 JComponent 负责在窗口关闭时杀死线程可能是一个想法。

【问题讨论】:

    标签: multithreading swing clojure dispose ownership


    【解决方案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)
    • 您关于dowhile 的其他观点当然是正确的。 :)
    • 我不认为我说你应该设置一个请取消标志。如果您的后台任务将在例如一千次迭代之后完成,或者当窗口关闭时,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。他这样做只是因为那里没有未来。
    • 为什么你认为这是个坏主意?
    猜你喜欢
    • 1970-01-01
    • 2013-07-17
    • 1970-01-01
    • 2013-02-22
    • 2017-10-17
    • 1970-01-01
    • 2014-03-12
    • 2021-02-26
    • 2013-01-28
    相关资源
    最近更新 更多