Upload
doanbao
View
224
Download
0
Embed Size (px)
Citation preview
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
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
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”
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)
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
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
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
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>>
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
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
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
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
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
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”)) }}
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) } }}
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) }
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())
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 } } }}
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”)
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
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) } }}
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) { }, ...
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 }}
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 { }
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
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, ...
28
Thread pools and work stealing
Thread Pool
task queue
task queue
task queue
task queue
worker threads (few)
Actors (many)
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}
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
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
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
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 } }) }}}
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