So before I get started, I'll describe how it works. You have these classes that inherit from Actor. In these classes you define an act() method. This is similar to the run() method in the Java Thread class.
When an actor receives a message, it goes into the actor's mailbox. You can check the mailbox with the receive() method.
How are we going to represent these messages? Well, a rather convenient approach is to use a feature of Scala: case classes. These are lightweight classes which basically just have a constructor and some public attributes. Kinda like structs from C, but with a constructor and inheritance.
So for my example, I'm coding a basic simulation environment. We want this simulation to be multi-threaded, so that we can take advantage of multiple CPUs in a machine. And we've decided to use Scala actors for it. This will be an agent-based simulation, so we will obviously need an Agent class. We will also have a Simulator class which drives the simulation.
Let's start with two types of messages. For each time step, the Simulator sends an Act message to each Agent. When the agent is done processing, it sends a DoneAct message back to the Simulator saying it is done. Since the number of Agents is fixed, let's just use a counter to keep track of the finished agents.
Here is the Simulator class:
class Simulator extends Actor {Our Agent class:
// need to override start to tell all our agents to start as well
override def start() : Actor = {
agents.foreach(agent => agent.start)
return super.start()
}
// something to add agents
def add(agent : Agent) = agents += agent
def act(){
// loop indefinitely
loop {
// check the mailbox
receive {
case Act => {
// this keeps track of how many agents we are waiting for
agents_left = agents.size
// the binary ! operator is what is used to send a message
agents.foreach(agent => agent ! Act)
}
case DoneAct = {
agents_left -= 1
// if we've gotten messages back from all agents, let's start
// the next simulation step
if (agents_left == 0)
this ! Act
}
}
}
}
var agents_left = 0
var agents = new LinkedList[Agent]()
}
class Agent(simulator : Simulator) extends Actor {This class is a lot simpler, mainly because we haven't actually defined anything to do yet.
def act() {
loop {
receive {
case Act => {
// do some actions
simulator ! DoneAct
}
}
}
}
}
For completeness, here are the Event classes:
abstract case class Event()And a main function:
case class Act() extends Event
case class DoneAct() extends Event
object Sim {So this simulation is pretty basic. In fact, it's pretty useless. It doesn't actually do anything. However this gives us a framework for creating a simulation. Let's tweak the agent class a little:
def main(args : Array[String]) {
var simulator = new Simulator()
// add a bunch of agents
simulator.add(new Agent(simulator))
simulator.add(new Agent(simulator))
simulator.add(new Agent(simulator))
simulator.add(new Agent(simulator))
// start the simulator, and send it an Act message
simulator.start
simulator ! Act
}
}
abstract class Agent(simulator : Simulator) extends Actor {We can now use our Agent class as a base class for other agents. I will end this post now, however next time I will make some subclasses of Agent to actually do something.
def act() {
loop {
receive {
case Act => perform()
}
}
}
def perform() {
// do nothing
}
}
2 comments:
hmm, this post is kinda old, but still.
I wanted to say that this way, you do not intend to use the message passing mechanism of scala for exchanging messages between agent, do you ?
Well, these guys have done it, and they concluded that the performence were worse than some other thread based solution
Authors : Aaron B. Todd and Amara K. Keller and Mark C. Lewis and Martin G. Kelly
title = {Multi-agent System Simulation in Scala: An Evaluation of Actors for Parallel Simulation},
Yeah I haven't actually done much work with Scala since this post was written (2009). I think a lot of interesting new technologies such as AMQP, Node.js have matured since then that are also interesting when it comes to highly-parallel computation.
Post a Comment