Files
atelier/domains/concurrency/patterns.md
T
2026-08-05 00:30:31 +00:00

2.7 KiB

Concurrency Patterns — Derived Rules

Derives from domains/concurrency/first-principles.md. Common concurrency patterns and when to use them.

Pattern 1: Message Passing (P5 Lock Minimization)

  • Threads/goroutines communicate via channels/queues, not shared memory.
  • "Don't communicate by sharing memory; share memory by communicating." (Go proverb)
  • Use when the data flows naturally in one direction. Avoids locks entirely.

Pattern 2: Read-Write Lock (P5 Lock Minimization)

  • Multiple readers, one writer. A RwLock allows concurrent reads, exclusive writes.
  • Use when reads vastly outnumber writes (e.g., a config cache).
  • Avoid when writes are frequent — the lock degrades to a mutex.

Pattern 3: Actor Model (P2 Single Responsibility)

  • Each actor owns its state. Actors communicate via messages. No shared state.
  • Use for isolated, long-lived workers (e.g., a session handler, a chat room).
  • Erlang/Akka/Pony are built on this. Implementable in any language with channels.

Pattern 4: Immutable Data Structures (P1 Immutability by Default)

  • Data is never mutated; a "change" creates a new value. Old values are safe to share.
  • Use in functional languages (Haskell, Clojure) or via persistent data structures (Immer.js).
  • Eliminates entire classes of races. The trade-off is allocation cost.

Pattern 5: Bounded Queue with Backpressure (P9 Bounded Queues)

  • A queue with a max size. When full, the producer is blocked or signaled.
  • Use to bound memory and propagate slowness from consumer to producer.
  • An unbounded queue hides a slow consumer until OOM. Always bound.

Pattern 6: Timeout on Every Block (P8 Timeout Discipline)

  • Every blocking call (lock acquire, queue send, HTTP request) has a timeout.
  • Use a timeout, not a forever-block. Forever is not a duration.
  • On timeout: cancel, retry, or fail. Do not hang.

Pattern 7: Cancellation Propagation (P7 Cancellation Support)

  • A cancellation signal propagates to all spawned work. Cancel the parent, the children stop.
  • Use context (context.Context in Go, AbortController in JS, CancellationToken in C#).
  • Cancellation is fast and complete. No orphaned goroutines/threads.

Pattern 8: Lock-Free Where Possible (P5)

  • Atomic operations (compare-and-swap) for simple state. No lock.
  • Use for counters, flags, simple pointers.
  • Avoid for complex state — lock-free code is subtle and easy to get wrong.

What Violates Concurrency Patterns

Violation Pattern
Shared mutable state with no lock (race, P1)
Unbounded queue P5 (OOM)
channel.send() with no timeout P6 (hang)
Spawned goroutine with no cancellation P7 (orphan)
A mutex held across an I/O call P3 (lock scope)
sync.Mutex for a counter P8 (use atomic)