Reputation:
As I write more core.async code, a very common pattern that emerges is a go-loop that alts over a sequence of channels and does some work in response to a message, e.g.:
(go-loop [state {}]
(let [[value task] (alts! tasks)]
...work...
(recur state))
I don't feel like I understand the tradeoffs of the various ways I can actually do the work though, so I thought I'd try to explore them here.
Is this summary correct and comprehensive?
Upvotes: 4
Views: 1149
Reputation: 10897
If the work to be done is entirely CPU-bound, then I would probably do it inline in the go
block, unless it's an operation that may take a long time and I want the go
block to continue responding to other messages.
In general, any work which doesn't block, sleep, or do I/O can be safely put in a go
block without having a major impact on the throughput of the system.
You can use >!
to submit work to a worker or pool of workers. I would almost never use >!!
in a go
block because it can block one of the finite number of threads allocated to running go
blocks.
When you need to do I/O or a potentially long-running computation, use a thread
instead of a go
. This is very similar to future
— it creates a real thread — but it returns a channel like go
.
put!
is a lower-level operation generally used at the "boundaries" of core.async to connect it to conventional callback-based interfaces. There's rarely any reason to use put!
inside a go
.
core.async can support fine-grained control over how threads are created. I demonstrated a few possibilities in a blog post, Parallel Processing with core.async.
Upvotes: 4