|
It is definitely in the "everyone should have it in their toolbelt" class. It is one of the fundamental primitives that makes multithreaded programming a sane and sensible thing that almost anyone can learn and do, rather than an insane hellscape of locks. It is not the only such primitive, but it is one of the basic ones. At the core, this is an actor: Just as an object fundamentally binds some bit of data to some set of methods that operate on it, an "actor" is a binding of a thread to some data, such that no other thread will ever access it. Therefore, there is no locking problem on that data and the actor has full control over it. It embeds an island of single-threaded processing in a sea of multithreading. It's a "server" with all the reductions in complexity you can apply to it for being in process rather than an external process communicated with via some stream. As with objects, you can elaborate on that theme in a number of ways. You can enforce at the language level that actors have unique access to certain data (Erlang/Elixir, Pony). You need some communication system; you can bless one as part of the language runtime (Erlang/Elixir mailboxes, Go channels). You can build in support for various structures on top of official actors just as you can build support for various object structures once you have an official concept of an object, like supervisor trees and crash notification (Erlang/Elixir). You can provide actor-level polymorphism by defining protocols they have to conform to. You can embed actors into a structured concurrency model (no language yet [1], as far as I know!) to create a "structured actor" model. However, just like objects, you get hordes of people insisting that their way is the one true way of doing actors and if you can't check off EVERY LAST BULLET POINT FEATURE of their preferred implementation you don't have actors. I imagine what you are picking up about actors are these people, who come away thinking that if you aren't Erlang, you aren't using actors. These people are wrong, just as people who think that if you aren't exactly Java you don't have objects. Actors at their core are very simple; really the only thing you have to have to get them right is to make sure that an actor's data is only accessed from one thread. Just as I tend to take an expansive view of "object orientation" that it is simply about having "methods" of some sort attached to data structures and I don't sit there and argue about how you need to have a "protected" key word and all the structure that implies to have an object system. While language support for completely rigid isolation of data to a thread is nice in some ways, it is not necessary, and it can also sometimes be a problem if you want to do something else; it is not hard to accomplish the goal with other tools. For instance, in most object oriented systems you can accomplish this with a factory/constructor function that creates an object and spawns its associated thread, using some variant of "private" to make it so only that thread ever receives access to the internal data. I'd even consider that second clause optional in the case of a language like Python, where private-ness is more convention than strict enforcement. But push comes to shove, nothing stops you from writing actors in C, just as you are not stopped from writing objects in C. You just get zero language support. In fact everything in the paragraph about what you can build on top of actors can be built as libraries. It's just that the tradeoffs vary from system to system. I implemented supervisor trees in Go, for instance. They aren't exactly what is in Erlang, but they largely solve the problems I have. A language may not have support for an "actor protocol" but using an object oriented language's concept of polymorphism allows a rough equivalent through implementing methods that encapsulate communication with the actor does roughly the same thing and solves the same problems. It isn't always perfect, but there's never a perfect solution to every problem in a given language. Having perfect actors implies some other weaknesses somewhere else. So, yes, the concept of binding data to threads is a critical one to building modern systems. While I generally phrase the errors of the 1990s threading hell world in terms of their locks, a complementary view is to say that the screw up of our first pass at threading, with the attendant fear still echoing through our community to this day, was the attempt to allow all threads access to all data all the time. When you don't do that, you don't get the threading hell of the 90s. Actors are not the only solution to the problem, but binding data to specific threads is one of the basic tools in solving it. Extremely important to sane multithreaded programming. [1]: Yes, this includes Erlang; in Erlang you can still "spawn" whenever you feel like it. It is true that usage of the OTP library encourages things that look like "structured concurrency" but it does not enforce it, nor does it take advantage of some of the things that structured concurrency would allow. And I'm talking about languages and their strict enforcement here, and as basic as OTP may be, it is still a library. |
http://dist-prog-book.com/chapter/3/message-passing.html