Actor Concurrency

Download Actor Concurrency

Post on 20-May-2015

4.702 views

Category:

Technology

2 download

Embed Size (px)

DESCRIPTION

An introduction to Erlang and the actor model of concurrency. Author: Alex MillerBlog: http://tech.puredanger.comTwitter: @puredanger

TRANSCRIPT

<ul><li> 1. Actor ConcurrencyAlex MillerBlog: http://tech.puredanger.com Twitter: @puredanger </li></ul> <p> 2. 8086 3. Moores Law? http://en.wikipedia.org/wiki/CPU_speed http://en.wikipedia.org/wiki/Transistor_count 4. Moores Law? http://en.wikipedia.org/wiki/CPU_speed http://en.wikipedia.org/wiki/Transistor_count 5. Moores Law? http://en.wikipedia.org/wiki/CPU_speed http://en.wikipedia.org/wiki/Transistor_count 6. State of the Practice public class Counter { private int count = 0;public int increment() { synchronized(this) { return ++count; } } } 7. JCIP says: Its the mutable state, stupid. Make elds nal unless they need to be mutable. Immutable objects are automatically thread-safe. Encapsulation makes it practical to manage the complexity. Guard each mutable variable with a lock. Guard all variables in an invariant with the same lock. Hold locks for the duration of compound actions. A program that accesses a mutable variable from multiple threads without synchronization is a broken program. Dont rely on clever reasoning about why you dont need to synchronize. Include thread safety in the design process - or explicitly document that your class is not thread-safe. Document your synchronization policy. 8. JCIP says: Its the mutable state, stupid. Make elds nal unless they need to be mutable. Immutable objects are automatically thread-safe. Encapsulation makes it practical to manage the complexity. Guard each mutable variable with a lock. Guard all variables in an invariant with the same lock. Hold locks for the duration of compound actions. A program that accesses a mutable variable from multiple threads without synchronization is a broken program. Dont rely on clever reasoning about why you dont need to synchronize. Include thread safety in the design process - or explicitly document that your class is not thread-safe. Document your synchronization policy. 9. The problem with shared state concurrency 10. The problem with shared state concurrency ...is the shared state. 11. Actors No shared state Lightweight processes Asynchronous message-passing Buffer messages in a mailbox Receive messages with pattern matching 12. The Basics 13. The Basicsspawn 14. The Basicssend spawn 15. The Basics receive 16. Erlang Invented at Ericsson Functional language Dynamic typing Designed for concurrency, fault-tolerance 17. Single assignment Eshell V5.6.3 (abort with ^G) 1&gt; A = 4. 4 2&gt; A. 4 3&gt; A = 6. ** exception error: no match of right hand side value 6 4&gt; A = 2*2. 4 18. Atoms and Tuples 1&gt; F=foo. foo 2&gt; Stooges = {larry,curly,moe}. {larry,curly,moe} 3&gt; Me = {person,quot;Alexquot;,quot;nachosquot;}. {person,quot;Alexquot;,quot;nachosquot;} 4&gt; {person,Name,Food} = Me. {person,quot;Alexquot;,quot;nachosquot;} 5&gt; Name. quot;Alexquot; 6&gt; Food. quot;nachosquot; 19. Lists 1&gt; List=[1,2,3]. [1,2,3] 2&gt; [First|Rest]=List. [1,2,3] 3&gt; First. 1 4&gt; Rest. [2,3] 5&gt; List2=[4|List]. [4,1,2,3] 6&gt; [Char|String] = abc. abc 7&gt; Char. 97 20. Functions 1&gt; Square = fun(X) -&gt; X*X end. #Fun 2&gt; Square(5). 25.3&gt; TimesN = fun(N) -&gt; (fun(X) -&gt; N*X end) 3&gt;end. #Fun 4&gt; lists:map(TimesN(10), [1,2,3]). [10,20,30] 21. Modules -module(mymath). -export([square/1,fib/1]).square(Value) -&gt; Value*Value.fib(0) -&gt; 0; fib(1) -&gt; 1; fib(N) when N&gt;0 -&gt; fib(N-1) + fib(N-2).1&gt; c(mymath.erl). 2&gt; mymath:square(5). 25 3&gt; mymath:fib(7). 13 22. Modules and Functions -module(mymath2). -export([fibtr/1]).fibtr(N) -&gt; fibtr_iter(N, 0, 1).fibtr_iter(0, Result, _Next) -&gt; Result; fibtr_iter(Iter, Result, Next) when Iter &gt; 0 -&gt; fibtr_iter(Iter-1, Next, Result+Next).1&gt; c(mymath2.erl). 2&gt; mymath2:fibtr(200). 280571172992510140037611932413038677189525 23. Concurrency Primitives Pid = spawn(fun) Pid ! message receive...end 24. Concurrency Primitives Pid = spawn(fun) Pid ! message receive...endPid 25. Concurrency Primitives Pid = spawn(fun) Pid ! message receive...endPid 26. Concurrency Primitives Pid = spawn(fun) Pid ! message receive...endPid 27. Concurrency Primitives Pid = spawn(fun) Pid ! message receive...endreceivePid ...end 28. A Simple Process loop() -&gt; receive {toF, C} -&gt;io:format(quot;~p C is ~p F~nquot;,[C, 32+C*9/5]),loop(); {toC, F} -&gt;io:format(quot;~p F is ~p C~nquot;,[F, (F-32)*5/9]),loop(); {stop} -&gt;io:format(quot;Stopping~nquot;); Other -&gt;io:format(quot;Unknown: ~p~nquot;, [Other]),loop() end. 29. Spawn! 1&gt; c(temperature). {ok,temperature} 2&gt; Pid = spawn(fun temperature:loop/0). 3&gt; Pid ! {toC, 32}. 32F is 0.0 C {convertToC,32} 4&gt; Pid ! {toF, 100}. 100C is 212.0 F {convertToF,100} 5&gt; Pid ! {stop}. Stopping {stop} 30. Responding -module(temp2). -export([start/0,convert/2]).start() -&gt; spawn(fun() -&gt; loop() end).convert(Pid, Request) -&gt; Pid ! {self(), Request}, receive {Pid, Response} -&gt; Response end. 31. Responding loop() -&gt; receive {From, {toF, C}} -&gt;From ! {self(), 32+C*9/5},loop(); {From, {toC, F}} -&gt;From ! {self(), (F-32)*5/9},loop(); {From, stop} -&gt;From ! {self(), stop},io:format(quot;Stopping~nquot;); {From, Other} -&gt;From ! {self(), {error, Other}},loop() end. 32. Responding 1&gt; c(temp2). {ok,temp2} 2&gt; Pid = temp2:start(). 3&gt; temp2:convert(Pid2, {toF, 100}). 212.0 33. Process Ring 34. Running Rings 1&gt; c(ring). 2&gt; T=ring:startTimer(). 3&gt; R=ring:startRing(100,T). spawned 100 in 241 microseconds 4&gt; R ! start. {1230,863064,116530} Starting message {1230,863064,879862} Around ring 10000 times {1230,863065,642097} Around ring 20000 times ...etc {1230,863140,707023} Around ring 990000 times {1230,863141,471193} Around ring 1000000 times Start={1230,863064,116875} Stop={1230,863141,471380} Elapsed=77354505 5&gt; R20k = ring:startRing(20000,T). spawned: 20000 in 118580 microseconds 35. Processes andMessages Processes cheap to create (10ks &lt; second) Create many processes (10ks to 100ks) Messages very fast to send (1M / second) 36. Error Handling 37. Error Handling link 38. Error Handling link 39. Error Handling exit signal 40. Linked Processes reportExit(WatchedPid) -&gt; spawn(fun() -&gt; process_flag(trap_exit, true), link(WatchedPid), receive {'EXIT', _Pid, Msg} -&gt; io:format(quot;got linked exit: ~p~nquot;, [Msg]) end end).startWatched() -&gt; spawn(fun() -&gt; receive die -&gt; exit(quot;diequot;); crash -&gt; erlang:error(quot;crashquot;) end end). 41. Linked Processes 1&gt; c(link). 2&gt; P1 = link:startWatched(). 3&gt; P2 = link:startWatched(). 4&gt; link:reportExit(P1). 5&gt; link:reportExit(P2). 6&gt; P1 ! die. got linked exit: quot;diequot; 7&gt; P2 ! crash. got linked exit: {quot;crashquot;,[{link,'- startWatched/0-fun-0-',0}]} 42. Does it work? 43. Ericsson AXD301 Telecom ATM switch Millions of calls / week 99.9999999% uptime = 32 ms down / year -&gt; Nortel Alteon SSL Accelerator 44. YAWS http://www.sics.se/~joe/apachevsyaws.html 45. Messaging 46. Facebook Chat For Facebook Chat, we rolled our own subsystem for logging chat messages (in C++) as well as an epoll- driven web server (in Erlang) that holds online users' conversations in-memory and serves the long-polled HTTP requests. Both subsystems are clustered and partitioned for reliability and efcient failover. Reference: http://www.facebook.com/note.php?note_id=14218138919 47. And others... 48. JVM-based language Object-functional hybrid Actor library 49. Scala Rings def act() { loop { react { case StartMessage =&gt; {log(quot;Starting messagesquot;)timer ! StartMessagenextNode ! TokenMessage(nodeId, 0) } case StopMessage =&gt; {log(quot;Stoppingquot;)nextNode ! StopMessageexit } 50. Scala Rings case TokenMessage(id,value) if id == nodeId =&gt; { val nextValue = value+1 if(nextValue % 10000 == 0) log(quot;Around ring quot; + nextValue + quot; timesquot;)if(nextValue == 1000000) { timer ! StopMessage timer ! CancelMessage nextNode ! StopMessage exit } else { nextNode ! TokenMessage(id, nextValue) } } case TokenMessage(id,value) =&gt; { nextNode ! TokenMessage(id,value) } } 51. Comparison to Erlang receive-based actors cheap to create (Erlang about 3x faster) Can also create many processes Messages also fast to send (Erlang about 2x faster) 52. Java-based framework with compile-time weaving Tasks - lightweight threads/processes/etc @pausable - let task be paused during execution sleep / yield Mailbox (typed via generics) - single consumer Messaging - mutable (in limited ways) 53. Kilim Ring import kilim.Mailbox; import kilim.Task; import kilim.pausable;public class NodeActor extends Task { private final int nodeId; private final Mailbox inbox; private final Mailbox timerInbox; private Mailbox nextInbox;public NodeActor(int nodeId,Mailbox inbox,Mailbox timerInbox) { this.nodeId = nodeId; this.inbox = inbox; this.timerInbox = timerInbox; } 54. Kilim Ring @pausable public void execute() throws Exception { while(true) { Message message = inbox.get(); if(message.equals(Message.START)) { timerInbox.putnb(Message.START); nextInbox.putnb(new TokenMessage(nodeId, 0)); } else if(message.equals(Message.STOP)) { nextInbox.putnb(Message.STOP); break;... 55. Kilim Ring } else if(message instanceof TokenMessage) { TokenMessage token = (TokenMessage)message; if(token.source == nodeId) { int nextVal = token.value+1; if(nextVal == 1000000) { timerInbox.putnb(Message.STOP); timerInbox.putnb(Message.CANCEL); nextInbox.putnb(Message.STOP); break; } else { token.value = nextVal; nextInbox.putnb(token); } } else { nextInbox.putnb(token); } } } 56. Performance10.0 400 7.5 300 millisecondsmilliseconds 5.0 200 2.5 10000 100 nodes 20k nodes130,000 Erlang97,500 millisecondsScala Kilim65,000 32,500 0 100M messages 57. http://tech.puredanger.com/presentations/actor-concurrency </p>