Concurrent and Actor Programming

Most paradigms describe one flow of control. Real programs must do many things at once: serve thousands of users, react to sensors, wait on the network. Concurrent programming is the paradigm that makes many independent activities the central idea, and the main design question is how they communicate.

The Problem: Shared Mutable State

The traditional model is threads sharing memory, protected by locks. It is fast, but two threads updating the same value can interleave and corrupt it (a race condition), and locks can wait on each other forever (a deadlock). These bugs depend on timing, so they are rare in tests and common in production.

Concurrency is not the same as parallelism. Concurrency is structuring a program as independent activities; parallelism is running them on several cores at the same time. A concurrent design can run on one core, and it is what lets a program use many cores safely.

Message Passing and the Actor Model

The alternative is to give up sharing. Each activity owns its data privately and communicates only by sending messages. In the actor model (Hewitt, 1973) an actor can do exactly three things when it receives a message: send messages to other actors, create new actors, and decide how it will behave for the next message.

  • Isolation — no other actor can touch an actor's state, so it needs no locks.
  • Mailboxes — messages queue up and are handled one at a time.
  • Location transparency — sending a message looks the same whether the receiver is local or on another machine.
  • Supervision — when an actor fails, another actor restarts it. The Erlang motto is "let it crash".

CSP: Communicating Sequential Processes

Tony Hoare's CSP (1978) is a close relative. Instead of addressing an actor, processes communicate through named channels: one process sends a value on a channel and another receives it. On an unbuffered channel the send and the receive meet, so channels synchronize as well as transfer data. Go's goroutines and channels are the best-known modern form.

Both models share the slogan "do not communicate by sharing memory; share memory by communicating".

Other Tools You Will Meet

  • Threads and locks — the low-level model of Java, C++ and C#, still needed for shared caches and performance-critical code.
  • async/await — cooperative concurrency in one thread for waiting on I/O (JavaScript, Python, Rust, C#).
  • Software transactional memory — shared memory with atomic transactions, as in Haskell and Clojure.
  • Ownership and capabilities — type systems such as Rust's and Pony's that reject data races at compile time.

Representative Languages

These four languages show the two big families: actors (Erlang, Elixir, Pony) and CSP (Go).

LanguageWhy study it
ErlangThe origin of practical actors: millions of lightweight processes, supervision and hot code loading.
ElixirActors on the same virtual machine with a modern syntax, macros and the Phoenix web framework.
GoCSP with goroutines and channels in a small, fast, compiled language.
PonyActors with reference capabilities so that the compiler proves the absence of data races.

The Trade-offs

Message passing is safe and scales across machines, but every interaction becomes a protocol. Messages must be copied or made immutable, ordering between many senders is not guaranteed, and debugging asks "who sent this?" instead of "who called this?". Choose it when isolation and failure handling matter more than the lowest possible latency.