Overview of Concurrency in clojure
Liverpool Clojure Dojo
3rd June 2013
@doppioslash
http://gplus.to/gattoclaudia
the old way
The Problems:
Deadlocks
Race conditions
Race conditions
The Old Solution:
Locking
The Culprit:
Mutable State
The Clojure Way
Avoid mutable state:
Immutable data structures
Side effects free
Where state cannot be eliminated:
Software Transactional Memory
...and Alfred North Whitehead's ideas.
Software Transactional Memory
Changes of state are wrapped in transactions, which ensure:
- Atomicity
either all changes of a transaction are applied or none.
- Consistency
only valid changes are committed.
- Isolation
no transaction sees the effect of other transactions.
- Durability
changes are persistent.
IDENTITY and value
Mutable state as an illusion:
it's actually a causally-linked succession of immutable values created by applying a function to the current value.
- Value
an immutable magnitude, quantity, number, or composite of these
- Identity
a series of causally related states over time
- State
value of an identity at a moment in time
- Time
relative ordering of causal values.
immutable
Applying a function that modifies a data structure will return a new data structure rather than modifying the old one
=> (def a-map {:key "value"})
#'user/a-map
=> (assoc a-map :another-key "more values")
{:key "value", :another-key "more values"}
=> a-map
{:key "value"}
Reference types
vars
refs
atoms
agents
vars
Per-thread local state.
The root binding is seen by all threads.
Rebound values are only seen by the local thread.
Since Clojure 1.3 needs ^:dynamic
Demonstration of thread-locality.
VARS
Rebinding a var
=> (def ^:dynamic a-var 42) #'user/a-var => a-var 42
=> (def ^:dynamic a-var 43) #'user/a-var => a-var 43
vars
Using binding
=> (defn print-var => ([] (println a-var)) => ([prefix] (println prefix a-var))) #'user/print-var => (bindind [a-var 44] => (print-var)) 44 nil
VARS
Demonstration that vars are thread-local
=> (import java.lang.Thread)
java.lang.Thread
=> (defn with-spawned-thread [fun]
=> (.start (java.lang.Thread. fun)))
#'user/with-spawned-thread
=> (do
=> (binding [a-var "rebound a-var"]
=> (with-spawned-thread
=> (fn [] (print-var "bg: ")))
=> (print-var "fg1: "))
=> (print-var "fg2: "))
bg: 42
fg1: rebound a-var
fg2: 42
nil
REFS
Synchronous and coordinated
multiple refs can be updated in one trasaction
wrapped in dosync
more than one change can be executed in the same transaction.
REFS
To mutate: alter and ref-set
=> (def a-ref (ref 42))
#'user/a-ref
=> (ref-set a-ref 43)
IllegalStateException No transaction running clojure.lang.LockingTransaction.getEx (LockingTransaction.java:208)
=> (dosync (ref-set a-ref 43))
43
=> (deref a-ref)
43
=> @a-ref
43
=> (dosync (alter a-ref inc))
44
ATOMS
Indipendent state
Uncoordinated
Atomic
Synchronous
State is mutated using a function
makes it safe to retry
atoms
To mutate: swap! and reset!
=> (def an-atom (atom 42))
#'user/an-atom
=> @an-atom
42
=> (swap! an-atom inc)
43
=> @an-atom
43
=> (swap! an-atom (fn [old] 44))
44
=> (reset! an-atom 42)
42
=> @an-atom
42
atoms
Update the value of an item inside a collection inside an atom
=> (def vector-atom (atom []))
#'user/vector-atom
=> (swap! vector-atom conj 42)
[42]
=> (swap! vector-atom conj 43)
[42 43]
=> (swap! vector-atom assoc-in [0] inc)
[43 43]
AGENTS
Asynchronous
Uncoordinated
Can be accessed from any thread
On error state the agent must be reset
AGENTS
To mutate send and send-off
=> (def an-agent (agent 42))
#'user/an-agent
=> @an-agent
42
=> (send an-agent inc)
#<Agent@492f96c3: 43>
Clojure ants
MORE
Futures
Promises
Pmap
Delays
References
Avout: distributed state in Clojure
Pluralsight Clojure Concurrency course
Clojuredoc's Concurrency and Parallelism in Clojure
Concurrency in Clojure
By Claudia Doppioslash
Concurrency in Clojure
- 4,256