1 / 52

Programming Paradigms for Concurrency

Programming Paradigms for Concurrency. Lecture 9 Part III – Message Passing Concurrency. Classical Shared Memory Concurrency - The Downsides. shared memory typically implies physically shared memory l ocks: the “ goto statements ” of concurrency

holly-case
Download Presentation

Programming Paradigms for Concurrency

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Programming Paradigms for Concurrency Lecture 9 Part III – Message Passing Concurrency

  2. Classical Shared Memory Concurrency -The Downsides • shared memory typically implies physicallyshared memory • locks: the “goto statements” of concurrency • OS threads are resource-hungry and context-switching is expensive • no natural adaptation to distributed computing • reasoning about programs is (even more) difficult • number of threads = number of available cores • ≠ number of logical tasks

  3. Message Passing Concurrency • no shared-memory (in its pure form) + some classes of concurrency errors avoided by design + natural programming model for distributed architectures - less efficient on shared-memory architectures: data must be copied before sending • synchronization between processes is explicit + reasoning about program behavior is simplified - some say: it’s harder to parallelize a sequential program using MP • higher level of abstraction + decoupling between computation tasks and physical threads possible -> event-based programming

  4. Message Passing Paradigms Two important categories of MP paradigms: • Actor or agent-based paradigms • unique receivers: messages are sent directly from one process to another • Channel-based paradigms • multiple receivers: messages are sent to channels that are shared between processes We will look at one programming language in each category.

  5. Reading Material • Actors in Scala. Haller and Sommers, Artima, to appear 2011 (preprint available). • Concurrent Programming in ML. Reppy, Cambridge University Press, 1999. • Communicating and Mobile Systems: The Pi Calculus. Milner, Cambridge University Press, 1999. Additional material will be posted on the lecture web site.

  6. The Actor Paradigm Actors are the object-oriented approach to concurrency “everything is an actor” actor = object + logical thread

  7. A Brief History of Actors • Hewitt, Bishop, Steiger 1973: proposal of actors • Greif 1975: operational semantics • Baker, Hewitt 1977: axiomatic semantics • Lieberman, Theriault 1981: Act-1 language • Clinger 1981: denotational semantics • Agha 1986: transition semantics • … • Armstrong et al. 1990s: Erlang language • … • Haller, Odersky 2007: actors in the Scala language

  8. Actors in a Nutshell • actors perform local computations and communicate via MP • communication is • asynchronous • buffered (unordered in theory but FIFO in practice) • over unique-receiver channels (mailboxes) • restricted to “known” actors • computation is • even-driven: react to incoming messages • dynamically create other actors • send messages to other actors • dynamically change behavior • languages supporting actor-based concurrency • Erlang • Salsa • Scala • many implementations in form of libraries B A A

  9. The Scala Language • unifies object-oriented and functional programming concepts • mixin class composition • higher-order functions • algebraic data types + pattern matching • closures • statically typed (type system based on System F) • interoperable with Java (compiles to the JVM) • enables embedding of rich domain-specific languages • Open-source: available from http://www.scala-lang.org

  10. Scala Actors[Haller, Odersky, 2007] • Scala library extension for high-level concurrent programming • part of the Scala standard library • pair of message receive operations (receive/react) • allows trade-off between efficiency and flexibility • react enables event-based programming without inversion of control • message handlers as first-class partial functions • enables extension of actor behavior • wide adoption • Lift web framework • Twitter

  11. Scala Actors through an Example

  12. Actor Chat ChatClient ChatRoom session ChatClient ChatClient ChatClient private state User subscribe to a chat room to receive chat messages. The chat room maintains a session of subscribers.

  13. Defining Actors importscala.actors.Actor classChatRoomextends Actor { def act() { // the actor’s behavior } } Actors are regular Scala objects that extend the Actor trait.

  14. Creating and Starting Actors object main extends Application{ valchatRoom = new ChatRoom chatRoom.start() // calls chatRoom.act }

  15. Communication in Actor Chat Subscribe user: User ChatRoom session Unsubscribe user: User ChatClient UserPost user: User post: Post private state private state All communication is via message passing.

  16. Messages case class User(name: String) case class Post(msg: String) abstract class Msg case class Subscribe(user: User) extendsMsg case class Unsubscribe(user: User) extendsMsg case class UserPost(user: User, post: Post) extendsMsg Any Scala object can serve as a message Good practice: use immutable case classes

  17. Defining the act Method classChatRoomextends Actor { def act() { while (true) { receive { case Subscribe(user) => // handle subscription message case Unsubscribe(user) => // handle unsubscribe request caseUserPost(user, post) => // handle a post from a user } } } } Defines a closure f • Actor reacts to incoming message via the receivemethod of the Actor trait • receive takes a partial function f as argument

  18. Defining the act Method classChatRoomextends Actor { def act() { while (true) { receive { case Subscribe(user) => // handle subscription message case Unsubscribe(user) => // handle unsubscribe request caseUserPost(user, post) => // handle a post from a user } } } } Defines a closure f • f maps incoming messages to the corresponding action performed by the actor • f is matched against the messages in the actor’s mailbox

  19. Defining the act Method classChatRoomextends Actor { def act() { while (true) { receive { case Subscribe(user) => // handle subscription message case Unsubscribe(user) => // handle unsubscribe request caseUserPost(user, post) => // handle a post from a user } } } } Defines a closure f • the first message on which f is defined is received • unmatched messages remain in the mailbox • if no message matches, receive blocks until a matching message is received

  20. Inplace Actor Definitions valchatRoom = actor { while (true) { receive { case Subscribe(user) => // handle subscription message case Unsubscribe(user) => // handle unsubscribe request caseUserPost(user, post) => // handle a post from a user } } } closure defines act method of chatRoom

  21. Handling Subscriptions varsession = Map.empty[Actor, Actor] while (true) { receive { case Subscribe(user) => valsessionHandler= actor { while (true) { self.receive { case Post(msg) => // forward message to subscribers } } } session = session + (subsriber-> sessionHandler) // handle UserPost and Unsubscribe message } }

  22. Sending Messages valchatRoom = newChatRoom chatRoom! Subscribe(User("Bob")) Method ! asynchronously sends a message to the recipient.

  23. Sending Messages valchatRoom = newChatRoom chatRoom!? Subscribe(User("Bob")) match { case response: String => println(response) } Method !? asynchronously sends a message and then blocks until a reply message has been received from the recipient.

  24. Handling Subscriptions varsession = Map.empty[Actor, Actor] while (true) { receive { case Subscribe(user) => val subscriber = sender valsessionHandler= actor { while (true) { self.receive { case Post(msg) => subscriber ! Post(msg) } } } session = session + (subscriber -> sessionHandler) reply(“subscribed: “ + user.name) // handle UserPost and Unsubscribe message } } address of the sender of matched message sends message to sender

  25. Message Timeouts with receiveWithin varsession = Map.empty[Actor, Actor] while (true) { receive { case Subscribe(user) => val (subscriber, room) = (sender, self) valsessionHandler= actor { while (true) { self.receiveWithin(1800 * 1000) { case Post(msg) => for (key <- session.keys; if key != subscriber) session(key) !Post(msg) case TIMEOUT => room ! Unsubscribe(user) case‘die => self.exit() } } } ... } }

  26. Processing Remaining Messages varsession = Map.empty[Actor, Actor] while (true) { receive { // handle Subscribe message case Unsubscribe(user) => session(sender) ! ‘die session = session – sender caseUserPost(user, msg) => session(sender) !msg } }

  27. Remote Actors

  28. Remote Actors Remote actors enable transparent communication between Scala actors over networks import scala.actors.Actor._ import scala.actors.remote.RemoteActor._ classChatRoom(port: Int) extends Actor { def act() { alive(port) register(‘chatRoom, self) // ... } } attach this actor to given port register given symbol with given actor

  29. Remote Actors Remote actors enable transparent communication between Scala actors over networks import scala.actors.Actor._ import scala.actors.remote.RemoteActor._ classChatClient(chatURL: String, chatPort: Int) extends Actor { def act() { val node = Node(chatURL, chatPort) valchatRoom = select(node, ‘chatRoom) chatRoom! Subscribe(User(“Bob”)) // ... } } obtain local interface to remote actor

  30. Event-Based Programming

  31. Event-Based Programming receive binds an actor to a dedicated JVM thread • if receive blocks, so does the dedicated thread of the blocking actor • each actor (blocking or nonblocking) needs its own thread • scales badly with the number of actors, since JVM threads are resource hungry Scala actors provide an alternative to receive, which enables event-based programming.

  32. receive vs. react react behaves like receive but with a different waiting strategy. • if react blocks, the actor is suspended and its dedicated thread released for processing events of other actors • event-based programming with react scales to large numbers of actors

  33. Event-Based Programming with react classChatRoomextends Actor { def act() { while (true) { react { case Subscribe(user) => // handle subscription message case Unsubscribe(user) => // handle unsubscribe request caseUserPost(user, post) => // handle a post from a user } } } } never returns!

  34. Event-Based Programming with react classChatRoomextends Actor { def act(): Unit = { react { case Subscribe(user) => // handle subscription message act() case Unsubscribe(user) => // handle unsubscribe request act() caseUserPost(user, post) => // handle a post from a user act() } } } closure must encompass full continuation of the actor

  35. Or Simpler... classChatRoomextends Actor { def act() { loop{ react { case Subscribe(user) => // handle subscription message case Unsubscribe(user) => // handle unsubscribe request caseUserPost(user, post) => // handle a post from a user } } } } behaves like while (true) { receive { ... }} special combinators for composing react blocks

  36. Lightweight Execution Environment Actors (many) Task queue Worker threads (few)

  37. Creating Actors actor { // body } closure => T3 T1 T2 T3 Events generate tasks

  38. Suspend in Event Mode Exception unwinds stack of actor/worker thread finishes current task Task Ti ... react { caseMsg(x) => // handle msg } defreact(f: PartialFunction[Any, Unit]): Nothing = { mailbox.dequeueFirst(f.isDefinedAt) match { case None => continuation = f; suspended = true case Some(msg) => ... } throw new SuspendActorException }

  39. Resume in Event Mode Actor asuspended with { caseMsg(x) => // handle msg } w.t. executes Ti Ti+2 Ti+1 Task Ti: ... a!Msg(42) ... Task Ti+2: .apply(Msg(42))

  40. Advanced Example

  41. Code Hot Swapping • payload of a message can be arbitrary scala object • actors can send new behavior to other actors • enables dynamic reconfiguration of actor behavior

  42. Hot Swapping Server case classHotSwap(code: PartialFunction[Any, Unit]) class Server extends Actor { def act = loop { react { genericBaseorElseactorBase } } private defactorBase: PartialFunction[Any, Unit] =  hotswapgetOrElse body  private varhotswap: Option[PartialFunction[Any, Unit]] = None private valgenericBase: PartialFunction[Any, Unit] = { caseHotSwap(code) => hotswap = code   }     def body: PartialFunction[Any, Unit] = { ... }  }

  43. Hot Swapping Server case classHotSwap(code: PartialFunction[Any, Unit]) class Server extends Actor { ... private valgenericBase: PartialFunction[Any, Unit] = { caseHotSwap(code) => hotswap = Some(code)   }     } Hot swapping the server: case object Ping ... val server = new Server server.start server !HotSwap({case Ping => println(“Ping”)}) server ! Ping

  44. Exception Handling and Monitoring

  45. Actors and Exceptions • act method of an actor may throw exceptions • unhandled exceptions do not seep out of the throwing actor • but unhandled exceptions terminate the throwing actor • other actors might wait for messages from actors that died silently • may cause deadlocks that are hard to debug

  46. Simple Exception Handling object A extends Actor { def act() { react { case 'hello => throw new Exception("Error!") } } override def exceptionHandler = { case e: Exception => println(e.getMessage()) } } scala> A.start() scala> A ! 'hello Error!

  47. Monitoring Actors Actor library provides special support for monitoring the life cycle of (a group of) actors: • exit(reason): terminates this actor • link(otherActor): links this actor with otherActor In case of termination/uncaught exceptions, exit is called implictely. Calls to exit are propagated to all linked actors. Enables • error propagation • delegated error handling • fault tolerance

  48. Error Propagation object Master extends Actor { def act() { Slave ! 'doWork receive { case 'done => throw new Exception("Master crashed") } } } object Slave extends Actor { def act() { link(Master) while (true) { receive { case 'doWork => println("Done") reply('done) } } } } Slave terminates, if Master crashes or terminates

  49. Delegated Error Handling val a = actor { receive { case 'start => valsomethingBadHappened = ... // some error condition if (somethingBadHappened) throw new Exception("Error!") println("Nothing bad happened") } } val b = actor { self.trapExit = true link(a) a ! 'start receive { case Exit(from, reason) if from == a => println("Actor 'a' terminated because of " + reason) } } calls to a’sexit method are converted to Exit messages in b’s mailbox

  50. More Features of Scala Actors • Futures • Pluggable schedulers • Unique references: race-free imperative actors • ...

More Related