Here we go with Week 4! My hope for this week is to get some practice greating larger programs with Clojure and to take a larger role contributing to Athens.
Chapter 9 is about the The Sacred Art of Concurrent and Parallel Programming.
- Concurrency - managing more than one task at the same time
- Parallelism - executing more than one task at the same time
(let [result (future (println "this prints once")
(+ 1 1))]
(println "deref: " (deref result))
(println "@: " @result))
- Once a future's body has been executed once, the result is cached
- On next deref, the println won't execute
- Derefing a future blocks if the future hasn't finished running
- place a time limit on how long to wait for a future
(deref (future (Thread/sleep 1000) 0) 10 5)
;; returns 10 if future doesn't return a value within 10 milliseconds
The signature for this looks like - ([ref] [ref timeout-ms timeout-val])
Futures - chuck tasks onto other threads. Clojure allows you to treat task defn and requiring the result independently with delays and promises.
-
First Concurrency Goblin Reference cell problem - occurs when two threads can read and write to the same location and the value of the location depends on the order of the reads and writes
-
Second Concurrency Goblin Mutual Exclusion - each thread is trying to write something to file but doesn't have exclusive write access. the output ends up being garbled because the writes are interleaved
-
Third Concurrency Goblin Deadlock - each thread blocks indefinitely for a resource to become available
Delays - define a task without having to execute it or require the result indefinitely.
(def jackson-5-delay
(delay (let [message "Just call my name and I'll be there"]
(println "First deref: " message)
message)))
Evaluate the delay and get result by deref or force
force
is identical to deref but communicates the intent more clearly causing a task to start as opposed to waiting for it to finish.
(force jackson-5-delay)
=> "First deref: Just call my name and I'll be there"
=> "Just call my name and I'll be there"
@jackson-5-delay
=> "Just call my name and I'll be there"
One way you can use a delay is to fire off a statement the first time one future out of a group of related futures finishes.
Promises allow you to express that you expect a result without having to define the task that should produce it or when that task should run
(def my-promise (promise))
(deliver my-promise (+ 1 2))
@my-promise
- Create a promise and deliver a value to it
- Can only deliver a value to a promise once
- Decouples the requirement for a result from how the result from how the result is actually computed. Can perform multiple computations in parallel.
- Future - Define a task and run it immediately on a different thread
- Delay - Define a task that doesn't get executed until later
- Promise = Express that you require a result without having to know about the task that produces that result
Concurrency and Parallelism allow us to take advantage of modern computing hardware to design programs that run efficiently.
Clojure has a lot of abstractions and tools that allow us programmers to not bogged down by the three goblins of concurrency.
- Future - Define a task and run it immediately on a different thread
- Delay - Define a task that doesn't get executed until later
- Promise = Express that you require a result without having to know about the task that produces that result
I didn't complete the chapter exercises for this chapter but it sounded pretty interesting and I want to tackle them soon.