+ All Categories
Home > Documents > Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing...

Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing...

Date post: 25-Aug-2020
Category:
Upload: others
View: 1 times
Download: 0 times
Share this document with a friend
37
Actor-Based Concurrency in Scala Philipp Haller EPFL Frank Sommers Artima
Transcript
Page 1: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

36

Upcoming book

http://www.artima.com

Page 37: Actor-Based Concurrency in Scala - Oracle€¦ · Actors in Erlang > “A pure message-passing language” (Joe Armstrong) ... scheduler execute {scheduler.actorGC.newActor(this)

Philipp Haller, [email protected]

Frank Sommers, [email protected]


Recommended