Micah
Micah

Reputation: 10395

Should one put `while true` inside of a clojure core.async thread?

I have this producer/consumer pattern i've been making with core.async thread functions like so:

(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)))))))))

However when i read the documentation for thread, it says:

Executes the body in another thread, returning immediately to the calling thread. Returns a channel which will receive the result of the body when completed.

It sounds like its expecting thread to be called one-off; not that its built for a while loop within it.

Should i not do while true in a thread block? Or will the thread be cleaned up when i close the thread's result chan?

Upvotes: 4

Views: 878

Answers (2)

Alan Thompson
Alan Thompson

Reputation: 29958

Please see the discussion here: http://www.braveclojure.com/core-async/

The most important sentence is:

The reason you should use thread instead of a go block when you’re performing a long-running task is so you don’t clog your thread pool.

Long-running tasks are exactly what Java threads are made for. Since you have a long-running task (it appears?) it should have it's own thread.

The JVM can handle a thousand user threads w/o problem on modern hardware.

Upvotes: 0

OlegTheCat
OlegTheCat

Reputation: 4513

The thread won't be cleaned up for sure, so, yes, probably you shouldn't use while true loop. Instead of it you may use loop with exit condition.

Common pattern is to make your go-routines (simply saying, loops that are executed on other thread) dependent on your input channels. In other words, when channel that provides data to go-routine is closed - go-routine is shut down.

I've rewritten your code a little bit, probably missed something, but I hope you'll get the idea:

(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))))))

Also I've replaced thread call with go call. This is "lightweight" version of thread, that uses thread parking instead of blocking native thread. If you need OS threads, you may replace it with (thread (loop ....

Upvotes: 1

Related Questions