In this blog, we are going to know why concurrent and parallel programming needs the Actor programming model.
Challenge of encapsulation in multithreaded environment
Encapsulation wraps object and their data into one unit so that they can not accessible directly from the outside. The object is responsible for exposing safe operations that protect the uniform nature of its encapsulated data.
This message sequence chart shows the interactions of method calls in OOP runtime behavior,. For example:
Unfortunately, the above chart does not accurately represent the lifelines of the objects during execution. In reality, a thread executes all these calls, and the enforcement of invariable occurs on the same thread that will invoke the method.
In the section of execution where two threads enter the same method. Unfortunately, the encapsulation model of objects does not guarantee anything about what happens in that section. Instructions of the two invocations can be interleaved in arbitrary ways which eliminate any hope for keeping the invariants intact without some type of coordination between two threads.
In Object-Oriented languages, we hardly think about threads or linear execution paths in general. We often imagine a system as a network of object instances that react to method calls, modify their internal state, then communicate with each other via method calls driving the whole application state forward:
How the Actor Model Meets the Needs of Modern, Distributed Systems
the actor model addresses the traditional system shortcomings in a principled way
- Enforce encapsulation without resorting to locks.
- Use the model of cooperative entities reacting to signals, changing state, and sending signals to each other to drive the whole application forward.
- Stop worrying about an executing mechanism which is a mismatch to our world view.
Usage of message passing avoids locking and blocking
Instead of calling methods, actors send messages to each other. Sending a message does not transfer the thread of execution from the sender to the destination. An actor can send a message and continue without blocking. Therefore, it can accomplish more in the same amount of time.
With objects, when a method returns, it releases control of its executing thread. In this respect, actors behave much like objects, they react to messages and return execution when they finish processing the current message. In this way, actors actually achieve the execution we imagined for objects:
Difference between message passing and calling methods
An important difference between passing messages and calling methods is that messages have no return value. By sending a message, an actor delegates work to another actor. In the context of illusion, it is expected a return value, the sending actor would either need a block or execute the other actor’s work on the same thread. Instead, the receiving actor delivers the results in a reply message.
The second key change we need in our model is to reinstate encapsulation. Actors react to messages just like objects “react” to methods invoked on them. The difference is that instead of multiple threads “protruding” into our actor and wreaking havoc to internal state and invariants, actors execute independently from the senders of a message, and they react to incoming messages sequentially, one at a time. While each actor processes messages sent to it sequentially, different actors work concurrently with each other so that an actor system can process as many messages simultaneously as the hardware will support.
Since there is always at most one message being processed per actor, the invariants of an actor can be kept without synchronization. This happens automatically without using locks:
In summary, this is what happens when an actor receives a message:
- The akka actor adds the message to the end of a queue.
- If the actor was not scheduled for execution, it is marked as ready to execute.
- A (hidden) scheduler entity takes the actor and starts executing it.
- Akka Actor picks the message from the front of the queue.
- Actor modifies internal state, sends messages to other actors.
- The akka actor is unscheduled.
What does Akka Actor have
- A mailbox – the queue where messages end up.
- A behavior – the state of the actor, internal variables etc.
- Messages – pieces of data representing a signal, similar to method calls and their parameters.
- An execution environment – the machinery that takes actors that have messages to react to and invokes their message handling code
Akka Actors handle error situations gracefully
There are two kinds of errors we need to consider:
- The first case is when the delegated task on the target actor failed due to an error in the task (typically some validation issue, like a non-existent user ID). In this case, the service encapsulated by the target actor is intact, it is only the task itself that is erroneous. The service actor should reply to the sender with a message, presenting the error case. There is nothing special here, errors are part of the domain and hence become ordinary messages.
- The second case is when a service itself encounters an internal fault. Actor creates another actor and becomes the parent of that new actor. This is very similar how operating systems organize processes into a tree. Just like with processes, when an actor fails, its parent actor can decide how to react to the failure. Also, if the parent actor is stopped, all of its children are recursively stopped, too. This service is called supervision and it is central to Akka.
This blog provided the reason behind adopting the Akka programming model by our modern system, it also explained the challenges that the object-oriented programming model faced in a multithreaded environment by using encapsulation and how Akka overcome that challenges by using message passing methods using actors.