go block vs thread in core.async
Asked Answered
R

2

9

From http://martintrojer.github.io/clojure/2013/07/07/coreasync-and-blocking-io/ :

To get a bit more concrete let's see what happens when we try to issue some HTTP GET request using core.async. Let's start with the naive solution, using blocking IO via clj-http.

(defn blocking-get [url]
  (clj-http.client/get url))


(time
   (def data
     (let [c (chan)
           res (atom [])]
       ;; fetch em all
       (doseq [i (range 10 100)]
         (go (>! c (blocking-get (format "http://fssnip.net/%d" i)))))
       ;; gather results
       (doseq [_ (range 10 100)]
         (swap! res conj (<!! c)))
       @res
       )))

Here we're trying to fetch 90 code snippets (in parallel) using go blocks (and blocking IO). This took a long time, and that's because the go block threads are "hogged" by the long running IO operations. The situation can be improved by switching the go blocks to normal threads.

(time
   (def data-thread
     (let [c (chan)
           res (atom [])]
       ;; fetch em all
       (doseq [i (range 10 100)]
         (thread (>!! c (blocking-get (format "http://fssnip.net/%d" i)))))
       ;; gather results
       (doseq [_ (range 10 100)]
         (swap! res conj (<!! c)))
       @res
       )))

What does it mean that "go block threads are hogged by the long running IO operations"?

Ruthi answered 20/5, 2015 at 17:29 Comment(1)
That links seems to be dead. This one works: martintrojer.github.io/clojure/2013/07/07/… (minus the trailing slash)Jazzy
C
6

Go blocks are intended to be a sort of light-weight cooperative threads; they provide thread-like behaviour with less overhead than full JVM threads by using a few threads in a pool and switching go blocks when they park - for instance, when waiting on a channel using <!. The thread-switching cannot work when you call a method in the block that blocks the JVM thread, so you quickly run out of JVM threads. Most standard Java (and Clojure) IO operations will block the current thread when waiting.

Coition answered 20/5, 2015 at 17:53 Comment(1)
I though clojure would also use ForkJoin but it looks like it's not. It uses a simple FixedThreadpool. Is there a reason for this? Scala forkjoins and can even tell the executer that it's blocking code so as to advise to spawn more threads. ( docs.scala-lang.org/overviews/core/futures.html ) and ( dev.clojure.org/display/design/Async+Executor )Massy
J
4

What does it mean that "go block threads are hogged by the long running IO operations"?

There are a limited number of threads dedicated to serving go blocks*. If you perform a blocking I/O operation on one of those threads, then it cannot be used for any other purpose until that operation completes (unless the thread is interrupted). This is also true for non-go block threads (i.e., threads that are returned from the thread function), but non-go block threads do not come from the limited go block thread pool. So if you do blocking I/O in a go block, you are "hogging" that go block's thread from being used by other go blocks, even though the thread isn't doing any actual work (it's just waiting for the I/O operation).

* That number currently happens to be 42 + the number of processors available to the JVM.

Jamestown answered 22/5, 2015 at 17:35 Comment(3)
Sorry to necro this, but isn't it 2 + available processors? Not 42 + ...?Corrasion
Is the go-block thread pool per-process or per-JVM or per-machine?Bise
What is the difference between per-process and per-JVM?Jamestown

© 2022 - 2024 — McMap. All rights reserved.