Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Are the clojure threads actually lightweight? Thread/sleep is blocking so you'd need to occupy 10 threads right? If you wanted to sleep without blocking a real thread would you use an executor service to write to a channel after delay and block on that?


Author here.

From what I've read/seen, the go blocks are lightweight thread-like processes multiplexed onto a thread pool. You may be correct about using Thread/sleep, ideally I would have used (timeout ...) and then pulled off the channel. However, I didn't want to introduce the concept of channels too early in the post, so I felt Thread/sleep worked as a compromise.


Thread/sleep will block the entire thread. The thread pool could create more threads, but core.async uses a fixed thread-pool, so, yeah, don't use Thread/sleep in a go block. A timeout channel would be the way to go, or, you can use an alternate core.async implementation, which is a (small) part of the Pulsar project: https://groups.google.com/forum/#!topic/clojure/1xxxTti6Vi0 (I'm the main author)

We will have full API and semantic compatibility when version 0.2 is released next week. Pulsar also has cluster distribution and an Erlang-like actor framework.

Because Pulsar uses instrumentation at the bytecode level, you have more freedom within Go blocks. You wouldn't use Thread/sleep in the Pulsar implementation, either, but Strand/sleep will do the job. It detects whether you're in a go block (implemented as a fiber in Pulsar), in which case it suspends the fiber but doesn't block the thread, or within a normal thread, in which case it will simply call Thread/sleep.


Yeah, Thread/sleep isn't quite what you want here:

(time (let [c (chan), n 1000] (dotimes [i n] (go (Thread/sleep 50) (>! c i))) (dotimes [i n] (<!! c))))

"Elapsed time: 8412.25733 msecs"

(defmacro gosleep [millis] `(<! (timeout ~millis)))

(time (let [c (chan), n 1000] (dotimes [i n] (go (gosleep 50) (>! c i))) (dotimes [i n] (<!! c))))

"Elapsed time: 91.278469 msecs"

ETA: for comparison, here's what happens if you actually make 1000 system threads:

(time (let [c (chan) n 1000] (dotimes [i n] (thread (Thread/sleep 50) (>!! c i))) (dotimes [i n] (<!! c))))

"Elapsed time: 4183.669835 msecs"


Thanks (and thanks to pron). I've updated the post to include a warning against using Thread/sleep in go blocks for "real" code.


I feel silly though that this is the one thing I've commented on, so:

This is an awesome post, thanks for it ^_^


Yah, for demo purposes of wanting to show something taking awhile thread/sleep is very convenient. I have no idea, time.sleep in golang might actually block a real thread too. I'm just curious if blocking in a go block could starve your thread pool or if there is some magic being done by the macro to even correct for that?


There isn't. Thread/sleep will block the thread and potentially starve your thread pool. Be careful not to do it.


Isn't this a major difference between core.async and go?

If you perform a blocking operation (Thread/sleep there, but could be, for example, a socket read?) will you run the risk of exhausting the thread pool?

The go runtime handles this by detecting how many threads are in blocking syscalls and spawning more as needed (which I guess could be undesirable).

If I've understood correctly, this is perhaps best summarised as "go makes your sync code run well in goroutines, with core.async you can block everything with sync syscalls in your go block"?

Or perhaps just "go will adjust the size of your thread pool dynamically, core.async requires it to be big enough"




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: