I’ve been told that (Scala) Actors never actually perform two operations at the same time, which suggests that the act (or react? or receive?) method is inherently synchronized. I know a long operation in an act method can cause blocking issues, and I assume that access to the message queue must be synchronized in some way… but…
What was suggested is that an actor receiving messages telling it to increment an internal counter would increment the counter in a threadsafe way. That no two update messages would be processed simultaneously, and so no two messages could attempt to update the counter at the same time.
A counter attribute in an actor sounds like “shared state.”
Is it really true that such an operation would be completely threadsafe? If so, how does an actor make use of multiple core machines in some efficient way? How is an actor multi threaded at all?
If not, what’s an appropriate idiomatic way to count messages in a threadsafe way without needing some synchronized/volatile variable?
The Actor model can be used to isolate mutable state from the outside world. When you have a mutable state (for example a global registry of IDs assigned to multiple concurrent processes), you can wrap that mutable state up inside an Actor and make the clients communicate with the Actor via message-passing. That way only the actor accesses the mutable state directly, and as you say, the client messages queue up to be read and processed one-by-one. It is important for messages to be immutable.
To avoid the queue getting full, it is important that the message processing (
react,receive, etc.) be as short as possible. Long-running tasks should be handed off to an other actor:Some alternatives in the process:
(S, result)back to A who forwards to SActorRef C => (Sender S, Message M)so in case it sees C fail, it can retry processing M with a new Actor.So to recap, an Actor is multi-threaded to the extent that multiple clients can send it multiple messages from various threads, and it is guaranteed that the Actor will process all these messages serially (although the ordering can be subject to various non-overly strict constraints).
Note that while the Actor’s
reactcode may be executed on various threads, in a single given point of time it is executed on a single given thread only (you can picture this that the Actor jumps from thread to thread as the scheduler sees fit, but this is a technical detail). Note: The internal state still doesn’t need syncronization, since Actors guarantee happens-before semantics between processing messages.Parallelism is achieved by having multiple Actors working in parallel, usually forming supervisor hierarchies or balancing workload.
Note that if all you need is concurrent/asynchronous computations, but you don’t have or can get rid of global state,
Futures are a better composing and easier concept.