There are no more exercises in Brave Clojure so my goal now is to finish a chapter a day so I can be done with the book soon. So today the expectation is Chapter 11.
go
creates a new process, and the go expression is called a go block. Everything in the go block runs concurrently, on a seperate thread. If you have a channel create by chan
, then evaluating (println (<! echo-chan))
expresses, "when I take a message from echo-chan
print it.
<!
is the take function, listens to the channel given as an arguments and evaluates when a new messages comes in. Double the!
and you can use it outside of the go block. With the double!
you go from parking to blocking.>!
is the put function(>!! echo-chan "stuff")
puts a message inecho-chan
and the thread is blocked until it's read. Double the!
and you can use it outside of the go block. And the code looks like this:
(def echo-chan (chan))
(go (println (<! echo-chan)))
(>!! echo-chan "ketchup")
; => true
; => ketchup
It is possible to buffer messages, by passing a number n
to the chan
function:
(def echo-buffer (chan 2))
(>!! echo-buffer "ketchup")
; => true
(>!! echo-buffer "ketchup")
; => true
(>!! echo-buffer "ketchup")
; This blocks because the channel buffer is full
This will be unblocked if another process takes a message from the channel. There are also sliding-buffer
s which are FIFO, and dropping-buffer
s which are LIFO.
threads
act almost exactly like futures: it creates a new thread and executes a process on that thread. Unlike future, instead of returning an object that you can dereference, thread returns a channel. When thread’s process stops, the process’ return value is put on the channel that thread returns:
The author of the book has a good explanation of why we should use thread
s over go blocks when running long computations:
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. Imagine you’re running four processes that download humongous files, save them, and then put the file paths on a channel. While the processes are downloading files and saving these files, Clojure can’t park their threads. It can park the thread only at the last step, when the process puts the files’ paths on a channel. Therefore, if your thread pool has only four threads, all four threads will be used for downloading, and no other process will be allowed to run until one of the downloads finishes.
alts!!
takes a vector of channels as argument and it will take the result of the first one to have anything on it. It is also possible to give it a timeout like this (alts!! [c1 (timeout 20)])
. alts!
is the parking alternative to alts!!
.
(fn symmetric? [tree]
(= tree ((fn mirror [t] (when t [(first t) (mirror (last t)) (mirror (second t))])) tree)))
Today I learned about core.async
allows me to create concurrent processes that respond to put and take communication events on channels. I also learned about go and thread. It's amazing that this library was inspired by Go's concurrency model.
Let's end the day with a tally of what I completed:
- Chapter 11 of Clojure for the Brave and True
- 4 4clojure problems (I am not done with the easy problems!)