- Transactional Memory
- Functional Languages and Monads
- Message Passing and the Actor Model
- Map Reduce
- No Silver Bullet
Message Passing and the Actor Model
One simple rules that you can use when writing parallel code makes it much easier to write code correctly: Nothing may be both aliased and mutable.
If two threads hold references to an object, that object must be immutable. If an object is mutable, it must be referenced by only one thread. This rule is very simple to say, but much harder to enforce in a language like C. Some languages, like Erlang, are designed around this idea.
Erlang is a language designed for concurrency. It has the distinction of being the only language I've ever used that let me write code that scaled cleanly up to 64 processors and worked the first time I tested it. In Erlang, you decompose your code into processes. These aren't operating system processes; instead, they're lightweightonly slightly more expensive to create than a function calland multiplexed onto OS threads or distributed throughout a cluster. Everything in Erlang is immutable except for the process dictionary, which is a key-value store associated with a process and therefore inherently unsharable. The keys and values stored in this dictionary are not mutable.
Erlang processes communicate by passing messages. Erlang inherits a lot from Prolog, including syntax, and most especially the pattern-matching facilities. One of my favorite aspects of Erlang is its ability to do pattern-matching on binary objects, which lets you implement low-level network protocol handlers trivially by using pattern-matching to decompose and route packets.
Erlang has no explicit synchronization. Messages are exchanged asynchronously. They're delivered in the order sent with respect to a pair of processes, but not necessarily with respect to a third. For example, if processes A and B both send two messages to process C, the two processes from A will arrive in the order in which they were sent, but the two from B may arrive before them, after them, or between them.
The actor model is very easy to use. Each process is sequential, and the only interactions between them are explicit message send-and-receive statements. Asynchronous messaging means that deadlocks are quite difficult. You only get a deadlock when two processes are exchanging messages and each is waiting for the other to complete. You can avoid this problem easily, by never blocking waiting for a specific message.
Rather than sit in a receive statement waiting for a reply to a message, it's customary to bundle all of the local state into the message. The process can then handle other messages and pick up where it left off when the reply arrives.
The actor model isn't limited to Erlang. Numerous other languages have been created to support it, including a dialect of Smalltalk, and you can implement it yourself very easily, using some of the techniques that we considered in part 2.