+ All Categories
Home > Documents > And You Concurrency, Clojure, · package clojure.lang; import...

And You Concurrency, Clojure, · package clojure.lang; import...

Date post: 18-Aug-2020
Category:
Upload: others
View: 4 times
Download: 0 times
Share this document with a friend
57
Clojure, Concurrency, And You Abhinav Sarkar (((((IN/Clojure Bengaluru 2019)))))
Transcript
Page 1: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Clojure,Concurrency,And You

Abhinav Sarkar

(((((IN/Clojure Bengaluru 2019)))))

Page 2: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Time

2

Page 3: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

(defn pass-time [person] (-> person (assoc :hair-color :gray) (update :age inc)))

3

Page 4: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Values and Identitiesuser=> (def abhinav {:hair-color :black :age 33})#'user/abhinav

4

Page 5: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Values and Identitiesuser=> (def abhinav {:hair-color :black :age 33})#'user/abhinav

user=> (def abhinav #_=> (atom {:hair-color :black :age 33}))#'user/abhinavuser=> (swap! abhinav pass-time){:hair-color :gray, :age 34}

4

Page 6: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Concurrency

5

Page 7: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Concurrency is a program-structuring technique in whichthere are multiple threads ofcontrol which execute "at thesame time".

— Simon Marlow, Parallel and ConcurrentProgramming in Haskell

6

Page 8: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Threads» Thread is a sequence of instructions along with a

context.» In case of Clojure on JVM, the threading model is

provided by the JVM which only supports OSthreads.

7

Page 9: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

fs.readdir(source, function (err, files) { if (err) { console.log('Error finding files: ' + err); } else { files.forEach(function (filename, fileIndex) { console.log(filename); gm(source + filename).size(function (err, values) { if (err) { console.log('Error identifying file size: ' + err); } else { console.log(filename + ' : ' + values); aspect = (values.width / values.height); widths.forEach(function (width, widthIndex) { height = Math.round(width / aspect); console.log('resizing ' + filename + 'to ' + height + 'x' + height); this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) { if (err) console.log('Error writing file: ' + err); }); }.bind(this)); } }); }); }});

8

Page 10: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

user=> (def t (Thread. #(println "hello")))#'user/tuser=> (.start t)hellonil

9

Page 11: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

user=> (import java.util.concurrent.Executors)java.util.concurrent.Executorsuser=> (def tp (Executors/newSingleThreadExecutor))#'user/tpuser=> (.submit tp #(println "hello"))hello#object[java.util.concurrent.FutureTask ...]

10

Page 12: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

user=> (future (println "hello"))hello#object[clojure.core$future_call$reify__6962 ...]

11

Page 13: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

user=> (future (println "hello"))hello#object[clojure.core$future_call$reify__6962 ...]

user=> (def f (future (do #_=> (println "hello") #_=> 12345)))hello#'user/fuser=> (deref f)12345

11

Page 14: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Synchronization

12

Page 15: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Synchronization» The process by which multiple threads agree on

some things at some time .» For example:

» timing: forking and joining threads» value of a variable» a sequence of steps to execute» access to a shared resource

13

Page 16: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

⌄ Time / Things > One value Multiple values

Synchronous Lock, Atom Multiple locks, Ref

Asynchronous Agent CRDTs, Raft/Paxos

14

Page 17: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Locks

15

Page 18: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Locks» An easy way of synchronization.» Prevent concurrent access to critical

sections/memory.» Do not compose.

16

Page 19: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

user=> (def lock (Object.))#'user/lockuser=> (locking lock #_=> (println "locked hello"))locked hellonil

17

Page 20: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

user=> (import java.util.concurrent.locks.ReentrantLock)java.util.concurrent.locks.ReentrantLockuser=> (def lock (ReentrantLock.))#'user/lockuser=> (try #_=> (.lock lock) #_=> (println "locked hello") #_=> (finally #_=> (.unlock lock)))locked hellonil

18

Page 21: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Atoms

19

Page 22: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Atoms» Atoms are references which change atomically and

immediately.» Simplest of all reference types.» Do not compose.

20

Page 23: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

user=> (def abhinav #_=> (atom {:hair-color :black :age 33}))#'user/abhinavuser=> (swap! abhinav pass-time){:hair-color :gray, :age 34}user=> (reset! abhinav {:hair-color :none :age 33}){:hair-color :none, :age 33}user=> @abhinav{:hair-color :none, :age 33}

21

Page 24: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

package clojure.lang;import java.util.concurrent.atomic.AtomicReference;

final public class Atom { private final AtomicReference state;

public Atom(Object o) { state = new AtomicReference(o); }

public Object deref() { return state.get(); }

public Object swap(IFn f) { for (;;) { Object v = deref(); Object newv = f.invoke(v); if (state.compareAndSet(v, newv)) { notifyWatches(v, newv); return newv; } } }}

22

Page 25: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Atom» Most ubiquitous concurrency feature used in

Clojure.» Use cases: dynamic con�gs, database connections,

simple caches.» Do not call swap with long running or non-

idempotent functions.

23

Page 26: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Agents

24

Page 27: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Agents» Agents, like Atoms, are references which support

atomic changes.» But the changes are made in an asynchronous

fashion.» Do not compose.

25

Page 28: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

user=> (def counter (agent 0))#'user/counteruser=> (dotimes [i 10] #_=> (send counter inc))niluser=> (await counter)niluser=> (println @counter)10nil

26

Page 29: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Agents are not Actors-module(counter).-export([loop/1]).

loop(N) -> receive {inc} -> loop(N+1); {get, Sender} -> Sender ! N, loop(N)end.

> Pid = spawn(counter, loop, [0]).> Pid ! {inc}.> Pid ! {get, self()}.> receive Value -> io:fwrite("~p~n", [Value]) end.1

27

Page 30: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Agents» Can be used for any state that does not require

strict consistency for reads:» Counters (e.g. message rates in event

processing)» Collections (e.g. recently processed events)

» Can be used for of�oading arbitrary computationsto a thread pool using send-via .

» Uses an unbounded queue, so too many functionsenqueued in it may cause OOM.

28

Page 31: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Refs

29

Page 32: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Refs and Software Transactional Memory» Refs allows changing multiple references together in a single

atomic operation.» Atomicity: All the state changes become visible to all the threads

at once.» Consistency: All the state changes can be validated before

allowing the transaction to commit.» Isolation: The atomic operation is completely unaffected by

whatever other threads are doing.

30

Page 33: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

31

Page 34: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;

class Account { private int id, amount; private Lock lock = new ReentrantLock();

Account(int id, int initialAmount) { this.id = id; this.amount = initialAmount; }

public void withdraw(int n) { this.lock.lock(); try { this.amount -= n; } finally { this.lock.unlock(); } }

public void deposit(int n) { this.withdraw(-n); }

public void transfer(Account other, int n) { this.withdraw(n); other.deposit(n); }}

32

Page 35: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

public void transfer(Account other, int n) { if (this.id < other.id) { this.lock.lock(); other.lock.lock(); } else { other.lock.lock(); this.lock.lock(); } try { this.amount -= n; other.amount += n; } finally { if (this.id < other.id) { this.lock.unlock(); other.lock.unlock(); } else { other.lock.unlock(); this.lock.unlock(); } }}

33

Page 36: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

(def account1 (ref 100))(def account2 (ref 100))

(defn withdraw [account amount] (alter account - amount))

(defn deposit [account amount] (withdraw account (- amount)))

(defn transfer [from to amount] (dosync (withdraw from amount) (deposit to amount)))

34

Page 37: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Source: www.sw1nn.com/blog/2012/04/11/clojure-stm-what-why-how/ 35

Page 38: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Source: mattias.niklewski.com/2014/04/stm.html 36

Page 39: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Clojure STM vs. Haskell STMimport System.IOimport Control.Concurrent.STM

type Account = TVar Int

withdraw :: Account -> Int -> STM ()withdraw acc amount = do bal <- readTVar acc writeTVar acc (bal - amount)

deposit :: Account -> Int -> STM ()deposit acc amount = withdraw acc (- amount)

transfer :: Account -> Account -> Int -> IO ()transfer from to amount = atomically $ do deposit to amount withdraw from amount

37

Page 40: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Refs» Best option for implementing in-memory transaction data stores

» Chat servers» Multiplayer games

» In-memory stream computation solutions.» A long-running transaction may re-execute many times because

it may be repeatedly aborted by shorter transactions.» Keeping history of values is expensive. Even more so when the

values are not persistent collections.

38

Page 41: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

core.async

39

Page 42: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

core.async and CommunicatingSequential Processes» Independent threads of activity.» Synchronous communication through channels.» Multiplexing of channels with alternation.

40

Page 43: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Source: arild.github.io/csp-presentation 41

Page 44: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

(go (println "hi"))

(def echo-chan (chan))(go (println (<! echo-chan)))(go (>! echo-chan "hello")); => hello

(def echo-chan (chan 10))

42

Page 45: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

(let [c1 (chan) c2 (chan) c3 (chan)] (dotimes [n 3] (go (let [[v ch] (alts! [c1 c2 c3])] (println "Read" v "from" ch)))) (go (>! c1 "hello")) (go (>! c2 "allo") (go (>! c2 "hola"))); => Read allo from #<ManyToManyChannel ...>; => Read hola from #<ManyToManyChannel ...>; => Read hello from #<ManyToManyChannel ...>

43

Page 46: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Multiplexinguser=> (def input (chan 1))#'user/inputuser=> (def broadcast (mult input))#'user/broadcastuser=> (dotimes [i 3] #_=> (let [output (chan 1)] #_=> (tap broadcast output) #_=> (go-loop [] #_=> (if-let [v (<! output)] #_=> (do (println i "Got!" v) #_=> (recur)) #_=> (println "Exiting")))))nil

44

Page 47: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Multiplexinguser=> (>!! input 42)true0 Got! 421

2 Got! Got!4242user=> (>!! input 43)true01 Got!Got! 43432Got! 43user=> (close! input)Exitingnil

ExitingExiting

45

Page 48: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Publish-Subscribeuser=> (def input (chan 1))#'user/inputuser=> (def p (pub input :tag))#'user/puser=> (let [c (chan 1)] #_=> (sub p :cats c) #_=> (go-loop [] #_=> (if-let [v (<! c)] #_=> (println "Cat guy got:" v) #_=> (println "Cat guy exiting"))))#object[clojure.core.async.impl.channels.ManyToManyChannel ...]user=> (let [c (chan 1)] #_=> (sub p :dogs c) #_=> (go-loop [] #_=> (if-let [v (<! c)] #_=> (println "Dog guy got:" v) #_=> (println "Dog guy exiting"))))#object[clojure.core.async.impl.channels.ManyToManyChannel ...]

46

Page 49: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Publish-Subscribeuser=> (defn send-with-tags [msg] #_=> (doseq [tag (:tags msg)] #_=> (println "sending... " tag) #_=> (>!! input {:tag tag :msg (:msg msg)})))#'user/send-with-tagsuser=> (send-with-tags {:msg "New Cat Story" :tags [:cats]})sending... :catsnilCat guy got: {:tag :cats, :msg New Cat Story}user=> (send-with-tags {:msg "New Dog Story" :tags [:dogs]})sending... :dogsnilDog guy got: {:tag :dogs, :msg New Dog Story}

47

Page 50: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Publish-Subscribeuser=> (let [c (chan 1)] #_=> (sub p :dogs c) #_=> (sub p :cats c) #_=> (go-loop [] #_=> (if-let [v (<! c)] #_=> (println "Cat/Dog guy got:" v) #_=> (println "Cat/Dog guy exiting"))))#object[clojure.core.async.impl.channels.ManyToManyChannel]

48

Page 51: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Pipelines(pipeline n to xf from close? ex-handler)(pipeline-async n to af from)(pipeline-blocking n to xf from close? ex-handler)

49

Page 52: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

func fibonacci(c, q chan int) { x, y := 0, 1 for { select { case c <- x: x, y = y, x+y case <-q: fmt.Println("quit") return } }}

func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit)}

core.async vs Goroutines

50

Page 53: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

core.async» Data transformation pipelines like ETL.» Multi-user chat servers, game servers.» More broadly, Staged Event Driven Architecture

programs.» With Clojurescript, it is a great replacement for

callback for UI interactions.

51

Page 54: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

core.async» Data transformation pipelines like ETL.» Multi-user chat servers, game servers.» More broadly, Staged Event Driven Architecture

programs.» With Clojurescript, it is a great replacement for

callback for UI interactions.» Doing blocking IO in go-threads blocks them.» Error handling is complicated.» Too many pending puts or take may throw errors.

51

Page 55: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object
Page 56: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

References» Clojure reference documentation» Clojure STM: What, why, how?» Thoughts on STM» Implementation details of core.async Channels» Core Async Go Macro Internals» Publish and Subscribe with core.async

53

Page 57: And You Concurrency, Clojure, · package clojure.lang; import java.util.concurrent.atomic.AtomicReference; final public class Atom {private final AtomicReference state; public Atom(Object

Thanks@abhin4v

abnv.me/ccy


Recommended