Part 5 · 1 chapters · ~8 min
Concurrency Models
Concurrency versus parallelism, threads and locks, event loops and async/await (colored functions), CSP with goroutines and channels, actors and supervision trees, virtual threads and structured concurrency, software transactional memory (Clojure, Haskell), data parallelism, and choosing a language by its concurrency model.
6
Five languages, one fan-out
code
// Node: async, one thread
const results = await Promise.all(ids.map(id => fetchBalance(id)));
// Go: goroutines and a channel
ch := make(chan Balance, len(ids))
for _, id := range ids { go func(id string) { ch <- fetchBalance(id) }(id) }
for range ids { results = append(results, <-ch) }
// Java 21: virtual threads with blocking calls
try (var ex = Executors.newVirtualThreadPerTaskExecutor()) {
var futures = ids.stream().map(id -> ex.submit(() -> fetchBalance(id))).toList();
for (var f : futures) results.add(f.get());
}
# Elixir: isolated processes, results as messages
results = ids |> Task.async_stream(&fetch_balance/1, max_concurrency: 50) |> Enum.map(fn {:ok, b} -> b end)
// Rust: async with tokio
let results = futures::future::join_all(ids.iter().map(|id| fetch_balance(id))).await;Coloured functions: in async/await languages, async functions can only be awaited from other async functions, splitting the ecosystem in two. Go, Java virtual threads and Erlang avoid the split by making blocking cheap.
CONCURRENCY MODELS AND THEIR LANGUAGES
one problem, five answers
swipe the figure sideways, or tap expand for full screen
1/5
threads and locks
The default in most languages and the source of most concurrency bugs: races and deadlocks (Concurrency course).
shared memory + locksraces and deadlocks