+ All Categories
Home > Documents > Asynchronous concurrency in Clojure and a few other languages

Asynchronous concurrency in Clojure and a few other languages

Date post: 18-Nov-2014
Category:
Upload: mh2295
View: 128 times
Download: 2 times
Share this document with a friend
Description:
A presentation for the Capital Area Clojure Group May 27, 2010:Concurrent programming n. a quest to achieve some perennial goals, by going beyond serial execution on a single processor.
32
ASYNCHRONOUS CONCURRENCY IN CLOJURE and (a few) other languages A Quest v 1.0 Michael Harrison Capital Area Clojure Group May 27, 2010 1
Transcript

ASYNCHRONOUS CONCURRENCY IN CLOJURE

and (a few) other languages

A Quest

v 1.0

Michael HarrisonCapital Area Clojure Group

May 27, 2010

1

Fun Fact: “Concurrent programming” means different things to

different people

“Multi-processor”

“Distributed”

“Parallel”

“Scalable”

2

Concurrent programming is not

a technology

a pattern

a methodology

3

Concurrent programming n. a quest to achieve some perennial goals, by going beyond serial

execution on a single processor.

“I wanna go fast!”

“I need more power”

“I wish I could get more done.”

4

There are many approaches to concurrency

• Shared-memory, coordinated threads

• Shared-nothing event-based processes

• Parallel decomposition (e.g. Map/Reduce)

• Pure functions and immutable data

• Delayed task execution

• Collaborative multitasking

• Blackboard-style message-passing

5

What is asynchronous concurrency?

It isn’t transactional (except...)

It isn’t linear in time

Code executions happening conceptually in parallel, with minimal coordination with each other

6

How’s it work in Clojure?

Clojure is built on the JVM. It carefully uses Java’s thread pools and related

concurrency constructs.

Soft, foam-covered corners

“You’ll shoot your eye out!”

The spectrum of Java thread-use danger

7

How’s it work in Clojure?

Clojure is built on the JVM. It carefully uses Java’s thread pools and related

concurrency constructs.

This book is like the secret history of Clojure

8

Clojure’s main players: agent and future

(def myagent (agent 0))(send myagent inc)(send myagent #(* 2 %))@myagent

(def myfuture (future (* 2 2)))@future

(def impure-future(future (my-side-effect-fn) (* 2 2)))

9

Agent

That creation and assignment occurs asynchronously. It won't be coordinated with your other code.

(send myagent inc)(send-off myagent #(* 2 %))

Agents are an identity with a value (state).(def myagent (agent 0))

You can create a new value from the old one, and assign it to the identity, by sending the agent a

transforming function.

@myagent ;=> ?

10

clojure.lang.Agent

The Agent class maintains two static Java Executor services, one for use with send, the other for send-off.

final public static ExecutorService pooledExecutor = Executors.newFixedThreadPool(2 + Runtime.getRuntime ().availableProcessors());

final public static ExecutorService soloExecutor = Executors.newCachedThreadPool();

Clojure’s Java implementation may change, of course, but it’s worth knowing the details.

11

clojure.lang.Agent

When send or send-off is applied to an agent and a transforming function, an inner class Action is used.

static class Action implements Runnable{

final Agent agent; final IFn fn; final ISeq args; final boolean solo;

void execute(){ try { if(solo) soloExecutor.execute(this); else pooledExecutor.execute(this); } ...

12

clojure.lang.Agent

The action’s run method is invoked by the Executor. It in turns calls a static doRun method:

static void doRun(Action action){ try {

... try { Object oldval = action.agent.state; Object newval = action.fn.applyTo( RT.cons(action.agent.state, action.args)); action.agent.setState(newval); action.agent.notifyWatches(oldval,newval); ...

13

Future

All subsequent dereferencing will return the value.

@future ; => 4@future ; => 4

(def myfuture (future (* 2 2)))

Asynchronous computations occurring in other threads.

Dereferencing a future will block if the expression has not finished.

more on future basics at Sean Devlin's Full Disclojure screencast channel:http://vimeo.com/channels/fulldisclojure

14

clojure.core/future-call

(future [body]) is a macro that wraps body as a function of zero args to clojure.core/future-call

(defn future-call ...  [^Callable f]  (let [fut (.submit clojure.lang.Agent/soloExecutor f)]    (reify      clojure.lang.IDeref       (deref [_] (.get fut))     java.util.concurrent.Future      (get [_] (.get fut))      (get [_ timeout unit] (.get fut timeout unit))      (isCancelled [_] (.isCancelled fut))      (isDone [_] (.isDone fut))      (cancel [_ interrupt?] (.cancel fut interrupt?)))))

15

Agent vs. Future

Agent Future

encapsulates state encapsulates a function call

may be repeatedly transformed may be evaluated only once

immediately returns a value blocks a thread

16

Because agents encapsulate state and may be repeatedly transformed, they are good for running

(repeated) computations that some other process is going to check on later (repeatedly).

Examples: Agents

Stuart Sierra’s “Agents of Swing” blog post is a great example. (Thanks, Sean Devlin)

17

Stuart Sierra’s “Agents of Swing” blog post

Examples: Agents

http://stuartsierra.com/2010/01/08/agents-of-swing

(defn new-flipper [] (agent {:total 0, :heads 0, :running false, :random (java.util.Random.)}))

(defn calculate [state] (if (:running state) (do (send *agent* calculate) (assoc state :total (inc (:total state)) :heads (if (.nextBoolean (:random state)) (inc (:heads state)) (:heads state)))) state))

18

Practically: Futures are good for decomposing larger computations.

pmap (as of Clojure 1.2) is implemented with futures.

Examples: Futures

19

pmap (as of Clojure 1.2)

Examples: Futures

(defn pmap...  ([f coll]   (let [n (+ 2 (.. Runtime getRuntime availableProcessors))         rets (map #(future (f %)) coll)         step (fn step [[x & xs :as vs] fs]                (lazy-seq                 (if-let [s (seq fs)]                   (cons (deref x) (step xs (rest s)))                   (map deref vs))))]     (step rets (drop n rets))))

20

Soft, foam-covered corners

“You’ll shoot your eye out!”

The spectrum of Java thread-use danger

Idiomatic Clojure’s exposure

Advantage: Clojure

For multithreaded programming, Clojure is powerful and much less painful than Java.

Clojure will let you do dangerous stuff if you really want to shoot your eye out.

21

Another Way?

Fact: Threads are fairly heavyweight.

Fact: A lot of program latency is blocking IO.

Fact: Threads still give some people the howling fantods.

22

Another Way?

What if I could interleave executions, without threading, so one could work while another blocked?

Compute

Compute

Compute Compute

Compute

Blocking operation Blocking operation

Blocking operation

23

Evented IO Concurrency

Blocking actions are given to a service handler. When the OS or network responds, the service handler

executes callback code.

In case you were curious, the service handler typically communicates with the OS via select, epoll, kqueue, dev/poll, etc. depending on the OS you’re running.

In the meantime, the service handler executes other code in a similar fashion, until it blocks or completes.

24

Evented IO in JavaScript: Node.js

http://nodejs.org/

JavaScript web programmers are used to specifying callbacks for DOM events or Ajax actions. Node

extends this capacity to IO events.

25

var tcp = require('tcp');var server = tcp.createServer(function (socket) { socket.setEncoding("utf8"); socket.addListener("connect", function () { socket.write("hello\r\n"); }); socket.addListener("data", function (data) { socket.write(data); }); socket.addListener("end", function () { socket.write("goodbye\r\n"); socket.end(); });});server.listen(7000, "localhost");

Evented IO in JavaScript: Node.js

26

Distributed Computing with Tuple Spaces

A client may write a tuple to the blackboard, suggesting a computation that needs to be done.

A server maintains a “blackboard” of tuples, arrays of mixed-type values.

Other clients may take a tuple that matches some criteria in order to perform the computation.

The computation’s result may then be written on the blackboard, within another tuple, for the original client

to take.

27

Tuple Spaces in Ruby: Rinda

while true tuple_space.write [:message, gets]end

while true puts tuple_space.take([:message, nil])[1]end

A one-sided conversation:

http://jpz-log.info/archives/2007/10/11/ruby-fun-with-rinda/

emitter process

receiver process

28

Oodles more

Don’t forget Erlang.

CouchDB’s replication and eventual consistency strategies put data and code “downstream” on your

machine.

“Ground-based computing” Some of CouchDB’s adherents use this term in contrast to cloud-based computing.

29

Sir Not-Appearing-in-This-Film

Measuring and improving performance

The power of reason and readability

30

Conclusions?

We aren’t there yet.

Frameworks, DSLs, and new langauges:Abstractions FTW

There’s probably a way for you to do it today

In general, good concurrent programming has a lot in common with plain old good programming.

Non-threaded methods may be better for networked applications

31

Shameless pitch

I’m looking for a gig.

[email protected]@goodmike

If you’re interested in what I know and like the way I think, do get in touch. :->

32


Recommended