Clojure concurrency overview
-
Upload
sergey-stupin -
Category
Technology
-
view
1.666 -
download
1
description
Transcript of Clojure concurrency overview
Agenda
-Overview-Clojure features-Concurrent programming-Vars-Refs and STM approach-Atoms-Agents
Overview
Rich Hickeyhttps://twitter.com/#!/richhickey
-about 2.5 years working on Clojure-first public release at 2007-interview: http://www.simple-talk.com/opinion/geek-of-the-week/rich-hickey-geek-of-the-week/
What is Clojure?
Clojure is a dynamic, LISP-like programming language that runs on the JVM
How does it look? Exactly! Like LISP
A form is a list where the first symbol in the list has to be a special word that the compiler can understand - usually the name of a function
Clojure Features
● Functional○ Immutable, persistent data structures○ Functions are objects
● Lisp● Hosted on JVM
○ the same memory model○ garbage collector○ etc
● Supporting Concurrency ● Open source
Clojure Features
● Dynamic development○ REPL (read-eval-print-loop), on-the-fly
compilation to JVM bytecode● Full set of immutable, extensible, read-only
data structures:○ lists○ maps○ sets○ vectors
● Macros
Clojure Features
Java interop
● Call java-methods, access fields● Functions implement java.util.concurrent.
Callable (defn func (smthng)) ● Proxy interfaces/classes● Primitive types - int, long, byte, Char, String etc● Clojure data structures implement Collection, Iterable,
Comparable etc
Concurrent programming
● Things are happening at the same time● Number of CPU is growing fast
Concurrent programming problems
Visibility problem● Multiple threads use shared data● 2 or more threads are trying to
change the same data at the same time
Concurrent programming problems
Basic solution is● locks● synchronized access● only one thread can have
lock, others blocks ● But it requires additional efforts
○ thread-coordination○ avoiding deadlocks
Concurrent programming problems
Access problem● several threads are trying to
access and change the same shared data at the same time
● write/read at the same time● readers block readers
Clojure way
● There is no any possibility to change data by direct access. All data structures are immutable, remember?
● Only read, if we add element - new data structure will be created.
● No locks● No access problems
But what if we really want change
data in Clojure?This opportunity is also
provided by the language! All hard work done for us.
Reference types
● Vars● Refs● Atoms● Agents
Vars
● Reference to mutable storage location ● Dynamically rebound on a per-thread basis● Functions are vars, so they can be dynamically rebound too● Ensure safe use via thread-isolation
(def x 5) // root-binding(x) // 5....(binding [x 20] (+ x 1)) // 21 ....(x) // 5 again, not changed
Changing Vars
(set! var-name new-value)● cannot change root-binding● works only in thread-local context (in "binding")
(def x 5) // root-binding(set! x 7) // exception will be thrown....(binding [x 20] (println (+ x 1)) // 21 (set! x 10) (+ x 5)) // 15....(x) // 5 again, not changed
Refs
● Based on Software Transactional Memory (STM)● Change the value by applying a function to old value ● Refs can be changed within a transaction only ● Transactions will be retried automatically if conflict happens● To make changes code must be surrounded with (dosync
...)● All changes are atomic
Refs
(def r (ref '(1 2 3))) (deref r) // list (1 2 3) (dosync // transaction begins .... (alter r // change ref! (fn [_] '(10 9 8)) ... ) // transaction ends (deref r) // list (10 9 8)
Refs lifecycle
(def r (ref '(1 2 3))) (deref r) (dosync .... (alter r (fn [_] '(10 9 8)) ... ) (deref r)
if any other transaction commutes - this transaction is repeated
Otherwise, that's ok and programs continues execution
Atoms
● Way to manage shared state synchronously�● Change the value by applying a function to old value● This is done in an atomic manner by function swap!�● Internally, swap! reads the current value, appliesthe
function to it, and attemprs to call compare-and-set! for this atom
Atoms best practices by Rich Hickey
(defn memoize [f] (let [mem (atom {})] (fn [& args] (if-let [e (find @mem args)] (val e) (let [ret (apply f args)] (swap! mem assoc args ret) ret)))))
Atoms best practices by Rich Hickey
(defn fib [n] (if (<= n 1) n (+ (fib (dec n)) (fib (- n 2))))) (time (fib 35))user=> "Elapsed time: 941.445 msecs" (def fib (memoize fib)) (time (fib 35)) user=> "Elapsed time: 0.044 msecs"
Agents
● Asynchronous changing. They are not blocking main execution, return immediately
● Change the value by applying a function to old value● Agent action dispatches take the
form (send agent fn args*)● �If other actions try to change data - they will be added to
the queue
Agents
(def a (agent 5)) // create agent(deref a) // 5
(send a (fn [old-val] (+ old-val 5)))(deref a) // may be 5, or may be 10(Thread/sleep 1000)(deref a) // 10
Useful links
● https://groups.google.com/forum/#!forum/clojure ● http://clojure.org ● http://alexott.net/ru/clojure/index.html ● http://www.lisperati.com/clojure-spels/casting.html ● http://www.pluralsight-training.
net/microsoft/courses/TableOfContents?courseName=clojure-concurrency-tutorial
Q&A