I just read through this:
https://clojure.org/guides/async_walkthrough
But I did not understand this sentence:
“Because these are blocking calls, if we try to put on an unbuffered channel, we will block the main thread.”
I think this makes the assumption that the channel was created on the main thread? If I create the channel in a thread would its use still block the main thread? If so, how?
Let me ask a different question now. This is more of a style question, or perhaps a question about architecture. The next part of the tutorial describes how we might use a core.async/thread, to do some work in a separate thread and then return a channel with the result. But when would we want to return a channel, instead of returning the result? The tutorial says:
"We can use thread
(like future
) to execute a body in a pool thread and return a channel with the result. Here we launch a background task to put “hello” on a channel, then read that value in the current thread.
(let [c (a/chan)]
(a/thread (>!! c "hello"))
(assert (= "hello" (<!! c)))
(a/close! c))
I’m trying to imagine the situation where I would want to do this? Why couldn’t I just use a Future? Is the idea that I would combine this with (a/alts!)
?
I also read this:
“The go
macro asynchronously executes its body in a special pool of threads. Channel operations that would block will pause execution instead, blocking no threads. This mechanism encapsulates the inversion of control that is external in event/callback systems.”
I’m not sure I fully understand the implication. Is this a simple win, in that I can use go
blocks and have activity that never blocks? But if the advantage is that simple, then why would we ever use threads? Is there a tradeoff that I need to consider? Because, otherwise, we could treat go
blocks as a complete replacement for most uses of threads. Is that a reasonable conclusion to make?
A final question about (a/alts!)
. In what situations would you use alts!!
in a regular thread? Why not always use alts!
in a go block? I can imagine using alts!!
on the main thread, and I’m wondering if that the most common use of it?
Finally, a completely different question, I once used Zach Tellman’s library, Manifold, on a small project:
core.async
has a different style, but seems to enable the same kind of workflow, in that put! and take! to a stream has the same workflow as putting and taking to a channel. Does anyone know of a situation where I would definitely prefer Manifold over core.async?