| name | concurrency |
| description | Concurrency patterns and pitfalls. Use when implementing parallel work, choosing sync primitives, auditing concurrent code, or reviewing for races and leaks. Pair with language-specific skill.
|
Concurrency
Don't communicate by sharing memory; share memory by communicating.
Concurrency is not parallelism.
When to Use
- Implementing worker pools, fan-out, pipelines, or rate limiting
- Choosing between message passing and shared state
- Auditing existing code for races, leaks, or deadlocks
- Reviewing concurrent code for correctness
When NOT to Use
- Single-threaded sequential code
- Simple request handlers with no background work
- Writing language-specific concurrent code (use
<lang>/concurrency skill)
Core Process
- Do you actually need concurrency? Sequential is simpler. Only add concurrency when there's a measurable benefit (I/O wait, CPU parallelism, latency requirement).
- Choose the primitive — message passing for data transfer, locks for data protection.
- Bound the concurrency — never spawn unlimited workers. Set limits.
- Design the shutdown path — every concurrent task must be cancellable and joinable.
- Protect shared state — if two tasks touch the same data, synchronize access.
- Test with race detection — race conditions are non-deterministic; tooling catches what reviews miss.
Decision: Message Passing vs Shared State
| Message Passing (channels/queues) | Shared State (locks/atomics) |
|---|
| Transferring ownership of data | Protecting internal state |
| Coordinating workers | Simple counter or flag |
| Distributing work | Short critical sections |
| Signaling events | Cache access |
Rule: transferring data → message passing. Protecting data → shared state.
Pattern Catalog
| Pattern | Use When | Key Rule |
|---|
| Worker Pool | Fixed concurrency over a stream | Bound the worker count |
| Fan-Out/Fan-In | Independent tasks, collect results | Each task writes to unique slot |
| Pipeline | Sequential stages connected by queues | Close/signal completion through the primitive |
| Rate Limiter | Throttle operations (API, DB) | Token bucket or leaky bucket |
| Once Init | Lazy init exactly once | Safe for concurrent callers |
| Deduplication | Suppress duplicate concurrent calls | Cache stampede prevention |
| Object Pool | Reuse temp objects under pressure | Reset before return; profile first |
Concurrency Audit Checklist
When reviewing concurrent code, check:
- Shutdown path — Can every concurrent task be stopped? Is there a join point?
- Bounded concurrency — Is the number of concurrent tasks limited?
- Shared state — Is every shared variable protected (lock, atomic, message primitive)?
- Context cancellation — Do loops check for cancellation?
- Error propagation — Do errors from concurrent tasks reach the caller?
- Resource lifecycle — Are connections/files closed even on cancellation?
Anti-Patterns
- Resource leak — task with no shutdown path (runs forever)
- Race condition — unsynchronized access to shared state
- Message passing misuse — using channels when a simple lock is clearer
- Unbounded concurrency — spawning unlimited tasks (memory exhaustion)
- Deadlock — circular wait on locks or full channels
- Fire and forget — launching tasks without waiting or error handling
Common Rationalizations
| Shortcut | Reality |
|---|
| "It probably won't race" | Races are non-deterministic. "Probably" means "sometimes in production." |
| "One concurrent task won't hurt" | Every unbounded task is a potential leak. Always provide a shutdown path. |
| "Message passing is always better" | Message primitives transfer ownership. Locks protect state. Pick the right tool. |
| "We'll add cancellation later" | Cancellation is architectural. Retrofitting is 10x harder than designing it in. |
Red Flags
- Concurrent task with no shutdown/cancellation mechanism
- Shared mutable state without synchronization
- Unbounded task spawning (could spawn millions)
- Message primitive used as a lock (wrong tool for the job)
- No race detection in test suite
- Error silently dropped from concurrent task
Verification