DOC: Improved Pi example. Removed latch and return result to listener actor. See #1729

This commit is contained in:
Patrik Nordwall 2012-01-26 11:57:59 +01:00
parent 5f4d566a8c
commit c64b73004a
2 changed files with 46 additions and 42 deletions

View file

@ -262,7 +262,7 @@ computation, creating a set of ``Worker`` actors. Then it splits up the work
into discrete chunks, and sends these chunks to the different workers in a
round-robin fashion. The master waits until all the workers have completed their
work and sent back results for aggregation. When computation is completed the
master prints out the result, shuts down all workers and then itself.
master sends the result to the ``Listener``, which prints out the result.
With this in mind, let's now create the messages that we want to have flowing in
the system. We need three different messages:
@ -272,6 +272,9 @@ the system. We need three different messages:
the work assignment
- ``Result`` -- sent from the ``Worker`` actors to the ``Master`` actor
containing the result from the worker's calculation
- ``PiEstimate`` -- sent from the ``Master`` actor to the
``Listener`` actor containing the the final pi result and how long time
the calculation took
Messages sent to actors should always be immutable to avoid sharing mutable
state. In scala we have 'case classes' which make excellent messages. So let's
@ -330,19 +333,8 @@ Here is the master actor:
A couple of things are worth explaining further.
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the
``Master`` actor. This latch is only used for plumbing (in this specific
tutorial), to have a simple way of letting the outside world knowing when the
master can deliver the result and shut down. In more idiomatic Akka code
we would not use a latch but other abstractions and functions like ``Future``
and ``?`` to achieve the same thing in a non-blocking way.
But for simplicity let's stick to a ``CountDownLatch`` for now.
Second, we are adding a couple of life-cycle callback methods; ``preStart`` and
``postStop``. In the ``preStart`` callback we are recording the time when the
actor is started and in the ``postStop`` callback we are printing out the result
(the approximation of Pi) and the time it took to calculate it. In this call we
also invoke ``latch.countDown()`` to tell the outside world that we are done.
Note that we are passing in a ``ActorRef`` to the ``Master`` actor. This is used to
report the the final result to the outside world.
But we are not done yet. We are missing the message handler for the ``Master``
actor. This message handler needs to be able to react to two different messages:
@ -355,7 +347,8 @@ The ``Calculate`` handler is sending out work to all the ``Worker`` via its rout
The ``Result`` handler gets the value from the ``Result`` message and aggregates it to
our ``pi`` member variable. We also keep track of how many results we have received back,
and if that matches the number of tasks sent out, the ``Master`` actor considers itself done and
invokes the ``self.stop()`` method to stop itself *and* all its supervised actors.
sends the final result to the ``listener``. When done it also invokes the ``context.stop(self)``
method to stop itself *and* all its supervised actors.
In this case it has one supervised actor, the router, and this in turn has ``nrOfWorkers`` supervised actors.
All of them will be stopped automatically as the invocation of any supervisor's ``stop`` method
will propagate down to all its supervised 'children'.
@ -365,6 +358,14 @@ Let's capture this in code:
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#master-receive
Creating the result listener
============================
The listener is straightforward. When it receives the ``PiEstimate`` from the ``Master`` it
prints the result and shuts down the ``ActorSystem``.
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#result-listener
Bootstrap the calculation
=========================
@ -380,9 +381,9 @@ start up the ``Master`` actor and wait for it to finish:
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#app
:exclude: actors-and-messages
As you can see the *calculate* method above it creates an ActorSystem and this is the Akka container which
As you can see the *calculate* method above it creates an ``ActorSystem`` and this is the Akka container which
will contain all actors created in that "context". An example of how to create actors in the container
is the *'system.actorOf(...)'* line in the calculate method. In this case we create a top level actor.
is the *'system.actorOf(...)'* line in the calculate method. In this case we create two top level actors.
If you instead where in an actor context, i.e. inside an actor creating other actors, you should use
*context.actorOf(...)*. This is illustrated in the Master code above.