Is it the supporting structure surrounding an actor focused library/language? Lots of person-hours have gone into making them to work well. Some home grown queue + pool of threads working off that queue... not so much.
Is it the supporting structure surrounding an actor focused library/language? Lots of person-hours have gone into making them to work well. Some home grown queue + pool of threads working off that queue... not so much.
Some examples:
- Some common patterns. E.g. actor hierachy and supervision, circuit breaker.
- Persistence. The actor persists its state (changes) and can wake up from a sleep.
- Clustering. The actor can live in another node, and you interact with it using the same API.
What I meant is that you can launch a co/go-routine and have a channel that only it can read. That is like 70% the power of having an actor system.
---
> an actor vs a queue with workers
See the sibling comment.
TLDR: Actors each has their own queue. The orderings in each queue are independent. This can help with parallelization if it fits the shape of the problem.
You can also iterate over an array in parallel and only modify the current element and that is like 70% of the power of having an actor system.
"Structured concurrency is a programming paradigm aimed at improving the clarity, quality, and development time of a computer program by using a structured approach to concurrent programming." - Wikipedia
In short: different computational model -- Actor model vs. Communicating sequential processes.
This really helped me: https://youtu.be/7erJ1DV_Tlo
Edit: added link to video
Now, using actors, this was trivial to do. Each actor was basically a glorified state machine, with each command, status update, etc, a message coming in that updated the internal state, and caused it to change state, and, where appropriate, update anything it needed to (such as the internal timer for when it would stop the job). We loaded up everything that had to happen in the next 12 hours, via an actor that would wake up and load more periodically (with an actor registry to find and confirm what existed already, as well as used to route incoming commands from users to change jobs). There is no complexity in managing a global state of priority between the actors; that's a 'solved problem' by the time slicing the underlying actor engine is doing for us; it's well tested, well proven, and invisible to us. Our implementation allows us to treat each job as existing in isolation, and write our code as though it and it alone is the job that needs to be executed. There is no global synchronization state to manage (well, with the caveat of taking the infinite list of future jobs, and ensuring we have actual processes equating to those scheduled to start in the next 12 hours, but that's a comparatively simple problem, and a proper use of sequential ordering. We want to execute on these jobs first), nor should their be based on the problem we're trying to solve.
Using a queue and traditional threading model though? Well, we'd need a synchronized priority queue, as items could have their priority changed at any time, from multiple sources. We'd also still need a registry. We'd need a pool of workers, that are mostly just blocking...hope we don't have > (number of workers) things needing to execute concurrently. Our complexity to manage all of this is high; the level of testing needed to validate that there aren't emergent bugs, also high. Our semi-realtime requirements are probably out the window (high contention on the queue, or high enough concurrency to exhaust the thread pool could lead to very measurable delays in updates). And ultimately, it all comes to the fact that the queue exists to pretend that the state of the world is sequential, and that we then had to then contort ourselves around to bring back to a state that allows for suitable concurrency. Updates to a job can end up killing a worker and inserting something back onto the queue, can remove something from the queue and reinsert it at a different position, etc, all while other workers are trying to access the queue simultaneously.
Actors are the simpler solution.
The comment about an actor's mailbox being a queue is missing the point (though, amusingly, demonstrating it). Queues are sequential. Actors are capable of doing only one thing at a time, and so when they are requested to do multiple things, they will do them sequentially. A mailbox filling with things that don't demand sequential processing is a code smell in an actor system. This is useful if you have limited resource access (queries on a DB connection, say), or need things to be done sequentially, and are very much akin to threads. But if you actually want multiple things concurrently...you should have multiple actors, not send them into the mailbox of one actor (which is very much the queue situation mentioned above; you imposed a sequential ordering on things you want done concurrently. Why?). That's the key difference; in most other languages, the queue is taking multiple things you actually want concurrently, and imposing a sequence to them. In the actor model, your queue is in fact things you DO want done sequentially; the things you want done concurrently should get federated out to multiple actors.