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.
By aaronontheweb · 1,458 installs
npx skills add aaronontheweb/dotnet-skills --skill csharp-concurrency-patterns
Source repository · Upstream listing
.NET Concurrency: Choosing the Right Tool
When to Use This Skill
Use this skill when:
Deciding how to handle concurrent operations in .NET
Evaluating whether to use async/await, Channels, Akka.NET, or other abstractions
Tempted to use locks, semaphores, or other synchronization primitives
Need to process streams of data with backpressure, batching, or debouncing
Managing state across multiple concurrent entities
Reference Files
[advanced concurrency.md](advanced concurrency.md): Akka.NET Streams, Reactive Extensions, Akka.NET Actors (entity per actor, state machines, cluster sharding), and async local function patterns
The Philosophy
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:
1. First choice: Redesign to avoid it (immutability, message passing, actor isolation)
2. Second choice: Use System.Collections.Concurrent (ConcurrentDictionary, etc.)
3. Third choice: Use Channel<T to serialize access through message passing
4. Last resort: Use lock for simple, short lived critical sections
Decision Tree
Level 1: async/await (Default Choice)
Use 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.
Level 2: Parallel.ForEachAsync (CPU Bound Parallelism)
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.
Level 3: System.Threading.Channels (Producer/Consumer)
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.
Level 4+: Akka.NET Streams, Reactive Extensions, Actors
For advanced scenarios requiring stream processing, UI event composition, or stateful entity management, see [advanced concurrency.md](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.
Anti Patterns: What to Avoid
Locks for Business Logic
Manual Thread Management
Blocking in Async Code
Shared Mutable State Without Protection
Quick Reference: Which Tool When?
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
The Escalation Path
Only escalate when you have a concrete need. Don't reach for actors or streams "just in case".