npx skills add ...
npx skills add aaronontheweb/dotnet-skills --skill csharp-concurrency-patterns
Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.
npx skills add aaronontheweb/dotnet-skills --skill csharp-concurrency-patterns
Use this skill when:
Start simple, escalate only when needed.
Most concurrency problems can be solved with async/await. Only reach for more sophisticated tools when you have a specific need that async/await can't address cleanly.
Try to avoid shared mutable state. The best way to handle concurrency is to design it away. Immutable data, message passing, and isolated state (like actors) eliminate entire categories of bugs.
Locks should be the exception, not the rule. When you can't avoid shared mutable state:
System.Collections.Concurrent (ConcurrentDictionary, etc.)Channel<T> to serialize access through message passinglock for simple, short-lived critical sectionsUse for: I/O-bound operations, non-blocking waits, most everyday concurrency.
Key principles: Always accept CancellationToken. Use ConfigureAwait(false) in library code. Don't block on async code.
Use for: Processing collections in parallel when work is CPU-bound or you need controlled concurrency.
When NOT to use: Pure I/O operations, when order matters, when you need backpressure.
Use for: Work queues, producer/consumer patterns, decoupling producers from consumers.
Channels are good for: Decoupling speed, buffering with backpressure, fan-out to workers, background queues.
Channels are NOT good for: Complex stream operations (batching, windowing), stateful per-entity processing, sophisticated supervision.
For advanced scenarios requiring stream processing, UI event composition, or stateful entity management, see advanced-concurrency.md.
Akka.NET Streams excel at server-side batching, throttling, and backpressure. Reactive Extensions are ideal for UI event composition. Akka.NET Actors handle entity-per-actor patterns, state machines with Become(), and distributed systems via Cluster Sharding.
| Need | Tool | Example |
|---|---|---|
| Wait for I/O | async/await | HTTP calls, database queries |
| Parallel CPU work | Parallel.ForEachAsync | Image processing, calculations |
| Work queue | Channel<T> | Background job processing |
| UI events with debounce/throttle | Reactive Extensions | Search-as-you-type, auto-save |
| Server-side batching/throttling | Akka.NET Streams | Event aggregation, rate limiting |
| State machines | Akka.NET Actors | Payment flows, order lifecycles |
| Entity state management | Akka.NET Actors | Order management, user sessions |
| Fire multiple async ops | Task.WhenAll | Loading dashboard data |
| Race multiple async ops | Task.WhenAny | Timeout with fallback |
| Periodic work | PeriodicTimer | Health checks, polling |
Only escalate when you have a concrete need. Don't reach for actors or streams "just in case".