5.2 Stateless Actors - Reference Documentation
Authors: The Whole GPars Gang
Version: 1.0.0
5.2 Stateless Actors
Dynamic Dispatch Actor
The DynamicDispatchActor class is an actor allowing for an alternative structure of the message handling code. In general DynamicDispatchActor repeatedly scans for messages and dispatches arrived messages to one of the onMessage(message) methods defined on the actor. The DynamicDispatchActor leverages the Groovy dynamic method dispatch mechanism under the covers. Since, unlike DefaultActor descendants, a DynamicDispatchActor not ReactiveActor (discussed below) do not need to implicitly remember actor's state between subsequent message receptions, they provide much better performance characteristics, generally comparable to other actor frameworks, like e.g. Scala Actors.import groovyx.gpars.actor.Actors import groovyx.gpars.actor.DynamicDispatchActorfinal class MyActor extends DynamicDispatchActor { void onMessage(String message) { println 'Received string' } void onMessage(Integer message) { println 'Received integer' reply 'Thanks!' } void onMessage(Object message) { println 'Received object' sender.send 'Thanks!' } void onMessage(List message) { println 'Received list' stop() } }final def myActor = new MyActor().start()Actors.actor { myActor 1 myActor '' myActor 1.0 myActor(new ArrayList()) myActor.join() }.join()
final Actor myActor = new DynamicDispatchActor().become { when {String msg -> println 'A String'; reply 'Thanks'} when {Double msg -> println 'A Double'; reply 'Thanks'} when {msg -> println 'A something ...'; reply 'What was that?';stop()} } myActor.start() Actors.actor { myActor 'Hello' myActor 1.0d myActor 10 as BigDecimal myActor.join() }.join()
final class MyDDA extends DynamicDispatchActor { void onMessage(String message) { println 'Received string' } void onMessage(Integer message) { println 'Received integer' } void onMessage(Object message) { println 'Received object' } void onMessage(List message) { println 'Received list' stop() } }final def myActor = new MyDDA().become { when {BigDecimal num -> println 'Received BigDecimal'} when {Float num -> println 'Got a float'} }.start() Actors.actor { myActor 'Hello' myActor 1.0f myActor 10 as BigDecimal myActor.send([]) myActor.join() }.join()
DynamicDispatchActor can be set to behave in a fair on non-fair (default) manner. Depending on the strategy chosen, the actor either makes the thread available to other actors sharing the same parallel group (fair), or keeps the thread fot itself until the message queue gets empty (non-fair). Generally, non-fair actors perform 2 - 3 times better than fair ones.Use either the fairMessageHandler() factory method or the actor's makeFair() method.
def fairActor = Actors.fairMessageHandler {...}
Static Dispatch Actor
While DynamicDispatchActor dispatches messages based on their run-time type and so pays extra performance penalty for each message, StaticDispatchActor avoids run-time message checks and dispatches the message solely based on the compile-time information.final class MyActor extends StaticDispatchActor<String> { void onMessage(String message) { println 'Received string ' + message switch (message) { case 'hello': reply 'Hi!' break case 'stop': stop() } } }
final actor = staticMessageHandler {String message -> println 'Received string ' + message switch (message) { case 'hello': reply 'Hi!' break case 'stop': stop() } }println 'Reply: ' + actor.sendAndWait('hello') actor 'bye' actor 'stop' actor.join()
Reactive Actor
The ReactiveActor class, constructed typically by calling Actors.reactor() or DefaultPGroup.reactor() , allow for more event-driven like approach. When a reactive actor receives a message, the supplied block of code, which makes up the reactive actor's body, is run with the message as a parameter. The result returned from the code is sent in reply.final def group = new DefaultPGroup()final def doubler = group.reactor { 2 * it }group.actor { println 'Double of 10 = ' + doubler.sendAndWait(10) }group.actor { println 'Double of 20 = ' + doubler.sendAndWait(20) }group.actor { println 'Double of 30 = ' + doubler.sendAndWait(30) }for(i in (1..10)) { println "Double of $i = ${doubler.sendAndWait(i)}" }doubler.stop() doubler.join()
import groovyx.gpars.actor.Actor import groovyx.gpars.actor.Actorsfinal def doubler = Actors.reactor { 2 * it }Actor actor = Actors.actor { (1..10).each {doubler << it} int i = 0 loop { i += 1 if (i > 10) stop() else { react {message -> println "Double of $i = $message" } } } }actor.join() doubler.stop() doubler.join()
public class ReactiveActor extends DefaultActor { Closure body void act() { loop { react {message -> reply body(message) } } } }
ReactiveActor can be set to behave in a fair on non-fair (default) manner. Depending on the strategy chosen, the actor either makes the thread available to other actors sharing the same parallel group (fair), or keeps the thread fot itself until the message queue gets empty (non-fair). Generally, non-fair actors perform 2 - 3 times better than fair ones.Use either the fairReactor() factory method or the actor's makeFair() method.
def fairActor = Actors.fairReactor {...}