Skip to content
Development
Skill

/dotnet-techne-csharp-concurrency-patterns

Use when choosing .NET concurrency patterns for async I/O, queues, pipelines, or thread safety. Keywords: async/await, channels, dataflow, Rx, lock contention, producer consumer, parallel processing.

From plugin
dotnet-episteme-skills
1314 skills13 agents3 commands3 hooks
+1

Claude Code

$ npx -y skills add Metalnib/dotnet-episteme-skills --skill dotnet-techne-csharp-concurrency-patterns --agent claude-code

SKILL.md

dotnet-techne-csharp-concurrency-patterns.SKILL.md
name: dotnet-techne-csharp-concurrency-patterns
description: "Use when choosing .NET concurrency patterns for async I/O, queues, pipelines, or thread safety. Keywords: async/await, channels, dataflow, Rx, lock contention, producer consumer, parallel processing."
disable-model-invocation: false
user-invocable: true
metadata:
  author: Metalnib
  version: "1.0.0"
  trigger_keywords:
    - async/await
    - channels
    - dataflow
    - rx
    - lock contention
    - producer consumer
    - parallel processing

.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, Dataflow, 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): TPL Dataflow, Reactive Extensions, hosted worker state patterns, 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, single-consumer 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

What are you trying to do?
│
├─► Wait for I/O (HTTP, database, file)?
│   └─► Use async/await
│
├─► Process a collection in parallel (CPU-bound)?
│   └─► Use Parallel.ForEachAsync
│
├─► Producer/consumer pattern (work queue)?
│   └─► Use System.Threading.Channels
│
├─► Need backpressure + multi-stage pipeline?
│   └─► Use TPL Dataflow
│
├─► UI event handling (debounce, throttle, combine)?
│   └─► Use Reactive Extensions (Rx)
│
├─► Coordinate multiple async operations?
│   └─► Use Task.WhenAll / Task.WhenAny
│
└─► None of the above fits?
    └─► Ask yourself: "Do I really need shared mutable state?"
        ├─► Yes → Consider redesigning to avoid it
        └─► Truly unavoidable → Use Channels or a dedicated single-consumer worker

---

Level 1: async/await (Default Choice)

**Use for:** I/O-bound operations, non-blocking waits, most everyday concurrency.

// Simple async I/O
public async Task<Order> GetOrderAsync(string orderId, CancellationToken ct)
{
    var order = await _database.GetAsync(orderId, ct);
    var customer = await _customerService.GetAsync(order.CustomerId, ct);
    return order with { Customer = customer };
}

// Parallel async operations (when independent)
public async Task<Dashboard> LoadDashboardAsync(string userId, CancellationToken ct)
{
    var ordersTask = _orderService.GetRecentOrdersAsync(userId, ct);
    var notificationsTask = _notificationService.GetUnreadAsync(userId, ct);
    var statsTask = _statsService.GetUserStatsAsync(userId, ct);

    await Task.WhenAll(ordersTask, notificationsTask, statsTask);

    return new Dashboard(
        Orders: await ordersTask,
        Notifications: await notificationsTask,
        Stats: await statsTask);
}

**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.

public async Task ProcessOrdersAsync(
    IEnumerable<Order> orders,
    CancellationToken ct)
{
    await Parallel.ForEachAsync(
        orders,
        new ParallelOptions
        {
            MaxDegreeOfParallelism = Environment.ProcessorCount,
            CancellationToken = ct
        },
        async (order, token) =>
        {
            await ProcessOrderAsync(order, token);
        });
}

**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.

public class OrderProcessor
{
    private readonly Channel<Order> _channel;

    public OrderProcessor()
    {
        _channel = Channel.CreateBounded<Order>(new BoundedChannelOptions(100)
        {
            FullMode = BoundedChannelFullMode.Wait
        });
    }

    // Producer
    public async Task EnqueueOrderAsync(Order order, CancellationToken ct)
    {
        await _channel.Writer.WriteAsync(order, ct);
    }

    // Consumer (run as background task)
    public async Task ProcessOrdersAsync(CancellationToken ct)
    {
        await foreach (var order in _channel.Reader.ReadAllAsync(ct))
        {
            await ProcessOrderAsync(order, ct);
        }
    }

    public void Complete() => _channel.Writer.Complete();
}

**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+: Dataflow, Reactive Extensions, and Hosted Workers

For advanced scenarios requiring stream processing, UI event composition, or stateful worker orchestration, see [advanced-concurrency.md](advanced-concurrency.md).

**TPL Dataflow** excels at server-sid

Read more
@metalnib
Ships withdotnet-episteme-skills

DotNet Episteme Skills - a curated, manual-first .NET AI skills library rooted in systematic knowledge (episteme) and shaped by disciplined craft (techne), designed for engineers who prioritise precision over hype.

1314 skills13 agents1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
13
Star history of Metalnib/dotnet-episteme-skills on GitHub, a chart by star-history.com

Other skills on dotnet-episteme-skills.

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills12d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc13d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react3d252k
@facebook@facebookInstall Skill

Questions about dotnet-techne-csharp-concurrency-patterns.

Which coding agents can use dotnet-techne-csharp-concurrency-patterns?
dotnet-techne-csharp-concurrency-patterns installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.