I’m getting all in a twist trying to get this to work. New to scala and to actors so may inadvertently be making bad design decisions – please tell me if so.
The setup is this:
I have a controlling actor which contains a number of worker actors. Each worker represents a calculation that for a given input will spit out 1..n outputs. The controller has to set off each worker, collect the returned outputs, then carry on and do a bunch more stuff once this is complete. This is how I approached it using receive in the controller actor:
class WorkerActor extends Actor {
def act() {
loop {
react {
case DoJob =>
for (1 to n) sender ! Result
sender ! Done
}
}
}
}
The worker actor is simple enough – it spits out results until it’s done, when it sends back a Done message.
class ControllerActor(val workers: List[WorkerActor]) extends Actor {
def act() {
workers.foreach(w => w ! DoJob)
receiveResults(workers.size)
//do a bunch of other stuff
}
def receiveResults(count: Int) {
if (count == 0) return
receive {
case Result =>
// do something with this result (that updates own mutable state)
receiveResults(count)
case Done
receiveResults(count - 1)
}
}
}
The controller actor kicks off each of the workers, then recursively calls receive until it has received a Done message for each of the workers.
This works, but I need to create lots of the controller actors, so receive is too heavyweight – I need to replace it with react.
However, when I use react, the behind-the-scenes exception kicks in once the final Done message is processed, and the controller actor’s act method is short-circuited, so none of the “//do a bunch of other stuff” that comes after happens.
I can make something happen after the final Done message by using andThen { } – but I actually need to do several sets of calculations in this manner so would end up with a ridiculously nested structure of andThen { andThen { andThen } }s.
I also want to hide away this complexity in a method, which would then be moved into a separate trait, such that a controller actor with a number of lists of worker actors can just be something like this:
class ControllerActor extends Actor with CalculatingTrait {
//CalculatingTrait has performCalculations method
val listOne: List[WorkerActor]
val ListTwo: List[WorkerActor]
def act {
performCalculations(listOne)
performCalculations(listTwo)
}
}
So is there any way to stop the short-circuiting of the act method in the performCalculations method? Is there a better design approach I could be taking?
EDIT: Have just been reading about Akka actors and spotted that they “guarantee message order on a per sender basis”. So I updated my example such that, if the controller needed to later ask the receiver for the computed value and needed to be sure it was all complete, it could do so with a message order guarantee on only a per sender basis (the example is still scala actors, not akka).
It finally hit me, with a bit of help from @Destin’s answer, that I could make it a lot simpler by separating out the part of the controller responsible for kicking off the workers from the part responsible for accepting and using the results. Single responsibility principle I suppose… Here’s what I did (separating out the original controlling actor into a controlling class and a ‘receiver’ actor):