【问题标题】:Should one put `while true` inside of a clojure core.async thread?是否应该将“while true”放在 clojure core.async 线程中?
【发布时间】:2016-08-28 21:05:15
【问题描述】:

我已经使用 core.async thread 函数创建了这个生产者/消费者模式,如下所示:

(defn -db-producer-factory [order-ids-chan next-chan]
  (thread
    (while true
      (do
        (let [order-id (<!! order-ids-chan)]
          (condp = order-id
            :finished (do
                        (>!! next-chan :finished))
            (supress-w-nextexc
              (->>
                ; get denorm'd order
                (-> (r/-get-order :live order-id)
                    denorm/order->denormalized)
                ; put in a map to avoid nils
                (hash-map :data)
                (>!! next-chan)))))))))

但是,当我阅读 the documentationthread 时,它说:

在另一个线程中执行主体,立即返回到 调用线程。返回将接收结果的通道 完成后的正文。

听起来它期望线程被一次性调用;并不是说它是为其中的while 循环而构建的。

我不应该在thread 块中执行while true 吗?或者当我关闭thread的结果chan时线程会被清理吗?

【问题讨论】:

    标签: multithreading clojure core.async


    【解决方案1】:

    线程肯定不会被清理,所以,是的,你可能不应该使用while true 循环。您可以使用带有退出条件的循环来代替它。

    常见的模式是让你的go-routines(简单地说,在其他线程上执行的循环)依赖于你的输入通道。换句话说,当向go-routine 提供数据的通道关闭时 - go-routine 将关闭。

    我稍微重写了你的代码,可能遗漏了一些东西,但我希望你能明白:

    (defn -db-producer-factory [order-ids-chan next-chan]
      (go-loop [order-id (<! order-ids-chan)]
        (condp = order-id
    
          nil
          ;; exiting
          nil
    
          :finished (do
                      (>! next-chan :finished)
                      (recur (<! order-ids-chan)))
          (do
            (supress-w-nextexc
             (->>
              (-> (r/-get-order :live order-id)
                  denorm/order->denormalized)
              (hash-map :data)
              (>! next-chan)))
            (recur (<! order-ids-chan))))))
    

    我还用go 调用替换了thread 调用。这是thread 的“轻量级”版本,它使用线程停放而不是阻塞本机线程。如果您需要操作系统线程,可以将其替换为(thread (loop ...

    【讨论】:

    • 我很难相信thread 中的loop 能够被处理/释放/收集垃圾,但你确实回答了我的问题!
    • 在 clojure #core-async slack room 中为我清理了它。谢谢!
    【解决方案2】:

    请在此处查看讨论:http://www.braveclojure.com/core-async/

    最重要的一句话是:

    你应该使用线程而不是 go 块的原因 执行长时间运行的任务是为了不阻塞线程池。

    长时间运行的任务正是 Java 线程的用途。既然你有一个长时间运行的任务(它出现了?)它应该有它自己的线程。

    JVM 可以在现代硬件上处理一千个用户线程而不会出现问题。

    【讨论】:

    • 问题不在于有一个长期运行的线程。这是关于拥有一个长时间运行的线程,即使不再需要它也会继续运行,这可能会导致内存泄漏。
    猜你喜欢
    • 2016-08-25
    • 2015-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多