Overview of Concurrency in clojure

Liverpool Clojure Dojo
3rd June 2013

@doppioslash
http://gplus.to/gattoclaudia

the old way

The Problems:
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"} ``

vars

The root binding is seen by all threads.

Rebound values are only seen by the local thread.

Since Clojure 1.3 needs ^:dynamic

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)
=> (defn with-spawned-thread [fun]
=>   (.start (java.lang.Thread. fun)))
=> (do
=>   (binding [a-var "rebound a-var"]
=>       (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>``````

MORE

Futures
Promises
Pmap
Delays

References

Avout: distributed state in Clojure
Pluralsight Clojure Concurrency course
Clojuredoc's Concurrency and Parallelism in Clojure