The 3 Goblins of Concurrency
Jul 13, 2020
# Contents
# What I learned Today
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.
# The Three Goblins of Concurrency
-
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
# Takeaways
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.