(Quick Reference)

5.2 Stateless Actors - Reference Documentation

Authors: The Whole GPars Gang

Version: 1.0.0-rc1

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.DynamicDispatchActor

final 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()

In some scenarios, typically when no implicit conversation-history-dependent state needs to be preserved for the actor, the dynamic dispatch code structure may be more intuitive than the traditional one using nested loop and react statements.

The DynamicDispatchActor class also provides a handy facility to add message handlers dynamically at actor construction time or any time later using the when handlers, optionally wrapped inside a become method:

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()

Obviously the two approaches can be combined:

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()

The dynamic message handlers registered via when take precedence over the static onMessage handlers.

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() } } }

Instances of StaticDispatchActor have to override the onMessage method appropriate for the actor's declared type parameter. The onMessage(T message) method is then invoked with every received message.

A shorter route towards both fair and non-fair static dispatch actors is available through the helper factory methods:

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()

Although when compared to DynamicDispatchActor the StaticDispatchActor class is limited to a single handler method, the simplified creation without any when handlers plus the considerable performance benefits should make StaticDispatchActor your default choice for straightforward message handlers, when dispatching based on message run-time type is not necessary. For example, StaticDispatchActors make dataflow operators four times faster compared to when using DynamicDispatchActor .

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()

Here's an example of an actor, which submits a batch of numbers to a ReactiveActor for processing and then prints the results gradually as they arrive.

import groovyx.gpars.actor.Actor
import groovyx.gpars.actor.Actors

final 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()

Essentially reactive actors provide a convenience shortcut for an actor that would wait for messages in a loop, process them and send back the result. This is schematically how the reactive actor looks inside:

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 {...}