-
Notifications
You must be signed in to change notification settings - Fork 1
ActorsInDetail
To get a feeling how actors work together have a look at the picture below. As stated actors are pieces of code that perform their work in one thread, independent and possibly in parallel with other actors. The thread as depicted as the green, downwards pointing arrows. Each actor labeled A,B etc has some operations inside and instructions with sending the results to other actors that may process them further.
For example, actor A performs some calculation, and then sends the result calc
to
actor B by message 1. This actor processes it further and sends the result res
to
both actor P by message 2 and Q by message 3. Subsequently those actors send their
results around, and some of them may reach actor R.
The idea of this model is that the problem at hand is broken down into small pieces that can be executed with the given information. This can completely be done within the actor, and thus within one thread. As soon as information from the outside world is required to proceed, the actor does not pause, but sends the partial result to a new actor, which will continue as soon as that extra piece of information is collected as well.
In this way it may be so that the majority of actors is waiting for information whereas some are actually running. In an ideal design, the number of actors capable of running equals the number cores/threads the hardware is able to provide. That way the complete task is processed in the quickest possible manner.
The library user/programmer of course has to decompose the complete task into small parts with the property of being independently executed, but never has to spend any thoughts on thread synchronization or carry futures around. Deadlocks can not occur (provided the programmer does not make explicit use of synchronization and only sends immutable data structures around)
Within each actor there exists some kind of structure to be able to perform the tasks. First, at creation, actors are often configured with immutable data from the outside world. This data is then passed as parameter to the constructor. Second, each actor is, after creation, brought to the state where it is able to perform the tasks that will enter the mailbox. This is done by startup code in the constructor that sets the some parameters to their correct value and is programmed by the library user.
Behind the scenes, the actor contains a mailbox which catches all messages send to the
actor, and calls a receive
method on each of them. This happens as soon as the
scheduler has a time slot available. User code processes the messages, and as a result
may change its internal state or send messages to other actors. Alternatively it may
also ignore the message altogether of course.
Further, all kinds of changes in the internal state of the actor may be communicated
to the user by making use of callbacks. These are methods that carry information if
the actor has been stopped for example, or if an exception did occur. The user can than
take appropriate action.
The way messages are handled between actors requires some special attention. If we take the message stream from the first paragraph and assume all actors are executed on a device with just one single core the following may be the situation.
The actors are executed in an ordered way like A,B,C ... P,Q,R. This implies that the message stream is predictable and that, after message 1 reaches B messages 2 and 3 reaches P and Q subsequently where P handles 2 first and produces 4, after which Q handles 3 and produces 5. Actor R will see the result 4 before 5 and process them in that order.
In case we have multiple cores in our system a different situation may arise.
In this example the first thread processes actors A,B and C and the second thread P,Q and R. Then it may happen that, at the moment actor B produces messages 2 and 3, actor Q is scheduled to run so that actor Q processes message 3 and produces 5 before actor P processes 2 and produces 4. In this situation actor R will process message 5 before 4.
So, in general there is no way for the user to know in which order messages will arrive in the mailbox. If a specific order is required, the actor must first collect all the messages that are expected, sort them, and subsequently process them in the required order.
There is one exception though. If an actor A sends two messages directly to the same actor P, the actor system guarantees that P will process them in the order that A send them.