Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing...
Transcript of Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing...
![Page 1: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/1.jpg)
Actor-Based Concurrency in Scala
Philipp HallerEPFL
Frank Sommers Artima
![Page 2: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/2.jpg)
2
Act I: Adventures in a concurrent world> Multi-core processors> Serial programs, parallel subsystems (SPPS)> Shared-nothing architecture> Simple message-passing paradigm> Asynchronous communication> Compose a reliable system out of many, possibly
unreliable, parts
![Page 3: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/3.jpg)
3
Actor principles> Actors are active objects> Each Actor has a mailbox> An Actor consumes interesting messages from its
mailbox, and performs some action based on the message
> Sequential code inside actions
Mailbox
MessageMessage Type
{ val a = … for (c b) ..← reply(d) ... }
MessageMessage Type
{ var tk = java.util. StringTokenizer( ...) tk.nextToken().. ... }
Sequential code
Sequential code
![Page 4: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/4.jpg)
4
Actor principles> Shared-nothing:
● Actor's state is private● No synchronization needed
> Actors communicate through messages● Messages are (mostly) immutable objects
> Actors can be local or remote● Same programming model● Scales “from multi-core to cloud”
![Page 5: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/5.jpg)
5
An established concurrency paradigm> Long history of research
● Starting at MIT in the 70's (Hewitt, Agha)● Inspired in part by Smalltalk and Simula
● AI languages requiring high degree of parallelism● Large-scale distributed systems
> Motivation for the development of Scheme● Started as a “toy” actors language● “The History of Scheme”, Guy Steele (JAOO'06)
![Page 6: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/6.jpg)
6
Actors in Erlang> “A pure message-passing language” (Joe
Armstrong)> Special, process-oriented virtual machine:
● Manages VM processes● Creating processes is very fast● Very large number of processes● Pattern matching
> OTP: library of Erlang modules for building reliable distributed systems
![Page 7: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/7.jpg)
7
Actors in Scala> Closest implementation of the Erlang model on
the JVM> Takes advantage of Scala language features
● Pattern matching, higher-order functions● Pure object model, traits and multiple inheritance● Full Java interoperability
> Event-based Actors● Not tied to Java threads● Scale to 100'000s Actors on a single JVM
![Page 8: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/8.jpg)
8
Actors in Scala> Local and remote
● Same programming style and semantics● Takes only a few lines of code to switch from local
to remote Actors> Implemented as an embedded DSL> Part of Scala's standard library
● scala.actors, scala.actors.remote> Used in practice on large Web sites
![Page 9: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/9.jpg)
9
Chat classes and message flow
ChatRoom
sessionUser → Actor
Username: String
Subscribe
user: User
Unsubscribeuser: User
Postmsg: String
PostFromuser: Userpost: Post
private stateChatClient
Subscribe: Add user to sessionUnsubscribe: Remove user from sessionPostFrom: Iterate through users, and send the Post to everyone, except to the sending user Post: Chat client to display the message. Handled by Actor that represents the user in the chat room
Actor<<extend>>
![Page 10: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/10.jpg)
10
Actor building blocks: creating an Actor
import scala.actors.Actor
class ChatRoom extends Actor {
def act() { // acting code comes here }}
object Chat { def main(args: Array[String]) { val chatroom = new ChatRoom chatroom.start() }}
> Any Scala object can be an Actor
> To create an Actor:● Extend Actor● Implement act()
> To start an Actor:● Call start()
> val: final variable> object: singleton
![Page 11: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/11.jpg)
11
Receiving messagescase class User(name: String)case class Subscribe(user: User)case class Unsubscribe(user: User)case class Post(msg: String)case class PostFrom(user: User, post: Post)
class ChatRoom extends Actor { def act() { var terminate = false while (!terminate) { receive { case Subscribe(user) => // Handle subscribe case Unsubscribe(user) => // Handle unsubscribe case PostFrom(user, msg) => // Process posts } } }}
> case class
● Supports pattern matching● Equality is structural● Default factory methods
> receive
● Removes next matching message from mailbox
● Actor suspends if no message matches
> var
● mutable variable
![Page 12: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/12.jpg)
12
Pattern matching
receive { case Subscribe(user) => println(user + “ subscribed”) case Unsubscribe(user) => println(user + “ unsubscribed”) }
case class Subscribe(user: User) case class Unsubscribe(user: User)
> List of alternatives: case pattern => expression> Expression is evaluated if pattern matches> Patterns are tried in order, the first match is evaluated> Similar to switch> Patterns look like factory method calls
![Page 13: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/13.jpg)
13
Handling subscriptionsvar session = Map.empty[User, Actor]
while (!terminate) { receive { case Subscribe(user) => val a = actor { while (true) { self.receive { case Post(msg) => println(msg) } } } session = session + (user -> a) println(“Sub “ + user) }}
> Maintain sessions in aMap[User, Actor]
> actor { } creates and starts a new Actor
> self returns the current Actor
> user a→ creates a map entry
![Page 14: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/14.jpg)
14
Sending a subscription messageobject Chat { def main(args: Array[String]) { val chatroom = new ChatRoom chatroom.start() chatroom ! Subscribe(User(“bob”)) }}
> ! method to send a message> Non-blocking, returns immediately> Implicitly passes a reference to the sender
![Page 15: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/15.jpg)
15
Handling chat messagescase class PostFrom(user: User, post: Post)
class ChatRoom extends Actor { var session = Map.empty[User, Actor] def act() { var terminate = false while (!terminate) { receive { case PostFrom(user, msg) => for (key session.keys; if key != user) {← session(key) ! msg } } } }}
object Chat { def main(args: Array[String]) { val chatroom = new ChatRoom chatroom.start() chatroom ! Subscribe(User(“bob”)) chatroom ! PostFrom(User(“mike”), Post(“hello there”)) }}
![Page 16: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/16.jpg)
16
Timeouts: receiveWithin (ms) { … }
> Waits for message with timeout (in ms)
> When timeout occurs, Actor receives a special TIMEOUT message
val a = actor { while (true) {
receiveWithin (1800 * 1000) { case Post(msg) => room ! PostFrom(user, msg) case TIMEOUT => room ! Unsubscribe(user) } }}
![Page 17: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/17.jpg)
17
Synchronous send
> !? synchronous send method> Blocks sender until receiver replies> Optionally specify timeout> Actor sends back a reply using reply(msg)
or sender ! msg
val chatroom = new ChatRoom chatroom.start() val r1 = chatroom !? Subscribe(User(“bob”)) println(r1)
(chatroom !? Subscribe(User(“mike”))) match { case res: String => println(res) }
![Page 18: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/18.jpg)
18
Futures
> !! method to send message and return a future> Does not block> Future: handle that represents the reply value> Sender waits until reply is available by invoking
the future with ()
val chatroom = new ChatRoom chatroom.start() val f1 = chatroom !! Subscribe(User(“bob”)) val f2 = chatroom !! Subscribe(User(“mike”))
println(f1()) println(f2())
![Page 19: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/19.jpg)
19
Creating remote Actors> alive(port):
Makes Actor remotely accessible on the specified port
> register( Symbol, Actor): Makes Actor referenceable by Symbol name
import scala.actor.remote._import RemoteActor._ class ChatRoom extends Actor { def act() { alive(8000) register('chatRoom, self)
var terminate = false while (!terminate) { receive { case Subscribe(user) => // Handle subscribe case Unsubscribe(user) => // Handle unsubscribe case PostFrom(user, msg) => // Process posts } } }}
![Page 20: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/20.jpg)
20
Sending messages to remote Actors
> Message-sending syntax identical to local Actors (!, !?, !!)
> select returns a proxy to the Actor> Reference to caller transfers to remote node:
● sender is valid reference in remote Actor> Transport-agnostic:
● TCP, JXTA, ...
val actor = select(Node(“other.node.address”, 8000), 'chatRoom)actor ! Post(“Hello there”)
![Page 21: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/21.jpg)
21
receive { } react { }receiveWithin() { } reactWithin() { }
while(cond) { } loopWhile(cond) { }
Event-based (aka thread-less) ActorsMaking your application scale
> Event-based actors do not consume a thread while waiting for a message
> Replace:
> Massive gains in scalability● 1'000'000 instead of 5'000 actors per JVM
![Page 22: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/22.jpg)
22
Event-based (aka thread-less) ActorsMaking your application scale
> Waiting no longer consumes a thread thanks to reactWithin { }
val a = actor { while (true) { receiveWithin (1800 * 1000) { case Post(msg) => room ! PostFrom(user, msg) case TIMEOUT => room ! Unsubscribe(user) } }}
val a = actor { loop { reactWithin (1800 * 1000) { case Post(msg) => room ! PostFrom(user, msg) case TIMEOUT => room ! Unsubscribe(user) } }}
![Page 23: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/23.jpg)
23
Lightweight sessions for the Actor chat
val a = actor { loop { react { case Post(msg) => ... } }}
> Restriction: react { } does not return> Use control structures provided by library
● andThen, loopWhile (cond) { }, ...
![Page 24: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/24.jpg)
24
When to use receive vs. react> Use react whenever possible> Use receive whenever necessary:
> For each task receive must return a result, therefore cannot use react
val tasks: List[Task]val results = tasks map { task => worker ! task receive { case Done(res) => res }}
![Page 25: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/25.jpg)
25
Act II: Under the hood> Event-based actors
● Decoupling threads and actors> Lightweight execution environment
● Work-stealing thread pool> Implementing the DSL
● Creating actors using actor { }● Receiving messages using react { }
![Page 26: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/26.jpg)
26
Event-based Actors> Naïve thread-per-actor implementation:
● Thread creation and context-switching overhead● High memory consumption
> Implementation in Scala Actors is event-driven:● Decouple Actors from JVM threads● Actors are executed on a thread pool
● Load balancing through work stealing● Wait for messages without consuming a thread
![Page 27: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/27.jpg)
27
Lightweight execution environmentLocal work queues and work stealing> Fast messaging, good locality
● Receiver of message is often executed on sender's thread
> Load balancing through work stealing> Advantages of thread pools
● Reduced memory consumption● Reusing existing threads amortizes thread
creation and tear-down costs● Graceful degradation, ...
![Page 28: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/28.jpg)
28
Thread pools and work stealing
Thread Pool
task queue
task queue
task queue
task queue
worker threads (few)
Actors (many)
![Page 29: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/29.jpg)
29
Implementing the DSLCreating Actors> Starting an Actor creates a task that is submitted
to a scheduler that manages a thread pooldef actor(body: => Unit): Actor = (new Actor { def act() { body } }).start()
val a = actor {
while (true) { react { case => ... } }
} def start(): Actor = { scheduler execute { scheduler.actorGC.newActor(this) (new Reaction(this)).run() } this}
![Page 30: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/30.jpg)
30
Implementing the DSLReceiving messages> Using react an event-based Actor waits for a
message without consuming a thread> If no message can be received:
1.Register the block of pattern matching cases following react as the actors continuation
2.Finish current task by throwing an exception● Unwinds pool thread's call stack
![Page 31: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/31.jpg)
31
val x = ...react { case y: Int => print(x + y)}doSomething(x) cont = {
case y: Int => print(x + y)}
> Then, unwind call-stack by throwing an exception> When Actor later resumes, only the saved closure
is executed, not doSomething(x)> Therefore, react is restricted: it never returns
> Assume react blocks> Save argument closure
in suspended Actor:
Implementing reactExample
![Page 32: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/32.jpg)
32
Customizing Actor execution> Runtime system may execute a single Actor on
several different threads over its lifetime> Need to customize execution for
● Use of thread-bound propertiesExample: ThreadLocals
● Integration with existing framework threadsExample: Swing/AWT event dispatch thread
> Actor subclasses may provide custom executors
![Page 33: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/33.jpg)
33
Example: executing Actors on the AWT event dispatch thread1.Override scheduler member in subclass of Actor
2.Implement SchedulerAdapter.execute():abstract class SwingActor extends Actor { override val scheduler = new SchedulerAdapter { def execute(block: => Unit): Unit = EventQueue.invokeLater(new Runnable() { def run() { block } }) }}}
![Page 34: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/34.jpg)
34
Where are Scala Actors used already?If they use it, you can, too!> Kestrel message queue system powering Twitter
(http://github.com/robey/kestrel/tree/master)
> Lift web framework (http://liftweb.net)
● Comet-style messaging> Scala OTP (http://github.com/jboner/scala-otp/tree/master)
● Erlang-style behaviors (e.g., supervisor hierarchies) and more
> partest● Test framework used for scalac and Scala
standard library
![Page 35: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/35.jpg)
35
Get involved!> http://www.scala-lang.org
![Page 36: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)](https://reader036.fdocuments.net/reader036/viewer/2022071100/5fd9382d90a58d281e67c5c9/html5/thumbnails/36.jpg)
36
Upcoming book
http://www.artima.com