| name | concurrency-and-threading-principles |
| description | Apply concurrency and parallelism patterns when implementing goroutines, async/await, threads, channels, mutexes, worker pools, or any concurrent data access. Covers race condition prevention, deadlock avoidance, and message passing. |
| user-invocable | false |
Concurrency and Threading Principles
1. Avoid Race Conditions
What is a race condition:
- Multiple threads access shared data concurrently
- At least one thread writes/modifies the data
- No synchronization mechanism in place
- Result depends on unpredictable thread execution timing
Prevention strategies:
- Synchronization: Locks, mutexes, semaphores
- Immutability: Immutable data is thread-safe by default
- Message passing: Send data between threads instead of sharing
- Thread-local storage: Each thread has its own copy
Detection:
- Go: Run with
-race flag (race detector)
- Rust: Miri tool for undefined behavior detection
- C/C++: ThreadSanitizer (TSan)
- Java: JCStress, FindBugs
2. Prevent Deadlocks
What is a deadlock:
- Two or more threads waiting for each other indefinitely
- Example: Thread A holds Lock 1, waits for Lock 2; Thread B holds Lock 2, waits for Lock 1
Four conditions (ALL must be true for deadlock):
- Mutual exclusion: Resources held exclusively (locks)
- Hold and wait: Holding one resource while waiting for another
- No preemption: Can't force unlock
- Circular wait: A waits for B, B waits for A
Prevention (break any one condition):
- Lock ordering: Always acquire locks in same order
- Timeout: Use try_lock with timeout, back off and retry
- Avoid nested locks: Don't hold multiple locks simultaneously
- Use lock-free data structures when possible
3. Prefer Immutability
- Immutable data = thread-safe by default (no synchronization needed)
- Share immutable data freely between threads
- Use immutable data structures where possible (Rust default, functional languages)
- If data must change, use message passing instead of shared mutable state
4. Message Passing Over Shared Memory
- "Don't communicate by sharing memory; share memory by communicating" (Go proverb)
- Send data through channels/queues instead of accessing shared memory
- Reduces need for locks and synchronization
- Easier to reason about and test
5. Graceful Degradation
- Handle concurrency errors gracefully (timeouts, retries, circuit breakers)
- Don't crash entire application on one thread failure
- Use supervisors/monitors for fault tolerance (Erlang/Elixir actor model)
- Implement backpressure for producer-consumer scenarios
Concurrency Models by Use Case
- I/O-bound: async/await, event loops, coroutines, green threads
- CPU-bound: OS threads, thread pools, parallel processing
- Actor model: Erlang/Elixir actors, Akka (message passing, isolated state)
- CSP (Communicating Sequential Processes): Go channels, Rust channels
Testing Concurrent Code
- Write unit tests with controlled concurrency (deterministic execution)
- Test timeout scenarios and resource exhaustion
- Test thread pool full, queue full scenarios
Related Principles
- Resource and Memory Management Principles @.claude/skills/resources-and-memory-management/SKILL.md
- Error Handling Principles @.claude/rules/error-handling-principles.md
- Testing Strategy @.claude/rules/testing-strategy.md