It often seems that whatever we see in Apple’s products today, we’ll see in PCs tomorrow. So with the introduction of the 8 core Mac pro, we’re already getting into some serious multi-core territory. And while it’s been harped on frequently, everyone is right; with many more cores we’ll need much more concurrency.
More and more, I’m coming to the conclusion that shared state multi-threading, at the very least, is an improper abstraction, and at the worst, is barely an abstraction at all. An interesting piece over at Raganwald today talks about dealing with mutable collections in these environments, drawing inspiration from functional languages such as Haskell. The posting talks about using a ingenious copy-on-write approach to speed up reads across multiple threads (I wonder, is this STM?). And while this technique is a leaps and bounds ahead of locking the entire data structure on each and every read and write, I wonder if we’re still not approaching this from the proper perspective.
Lately I’ve been interested in the Actors model as an alternative to this shared state business. Though I’ve much more to learn, it seems that the spirit of the actors model is interacting processes (lightweight threads really) that communicate via message passing. The actors react to incoming messages and change state, fire off new messages, etc. The important thing, however, is that shared-state is gone (shared-nothing). When done properly, the actor only explicitly calls code that is entirely within its scope (thread local, if you will) and uses messages to carry out all of its other work. Though this shared-nothing model is used in Erlang without objects, with some discipline it’s not necessary to throw out OOP.
While the terminology seems to have faded, in Smalltalk you don’t ‘invoke methods’ but rather ‘send messages’. We can use this idea when thinking about actors in a more familiar Java-esque object oriented setting. Consider an actor-object: an object with it’s own thread that responds to incoming messages. An actor-object can still hold references to local objects and ‘invoke methods’ on them, but these references should be totally encapsulated and never leaked to other actor-objects (a stronger form of composition for the UML inclined). That is, the actor-object though it likely could, shares nothing with other actor-objects in the system by way of object references. One actor-object wishing to collaborate with with another must use the ‘message sending’ concept. Notice that all concurrency issues are abstracted away. The actor-object has only one thread that is woken up when messages arrive, and this is the only thread that ever executes the actor’s code.
Much of this can be implemented today on the JVM using Scala, it’s actors library, and some discipline. However, we must ultimately decide whether or not this approach is any simpler (or more productive) than standard shared-state/locking approach. Eventually we may throw out the OOP model altogether (referential transparency and concurrency anyone?), but until then we should remember that OOP, while no cure-all-snake-oil, has a lot of warm brains and bodies behind it. We should, at the very least, strive to simplify concurrency in this nearly ubiquitous approach. We need out of the multi-core-concurrency-crisis.
As an aside, I’ve been dealing with these problems (and using Scala) in my work on CN. I hope to post more of my thoughts as I continue to tackle the concurrency beast.