2.7 KiB
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
RwLockallows 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.Contextin Go,AbortControllerin JS,CancellationTokenin 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) |