From kagents
C# concurrency patterns — async/await, Channel<T>, SemaphoreSlim, CancellationToken, Task.WhenAll, parallel execution, producer/consumer, rate limiting. USE FOR: implementing async workflows, choosing concurrency primitives, fixing deadlocks or async anti-patterns. DO NOT USE FOR: general C# syntax (use csharp-patterns) or EF Core query parallelism (use database-performance).
npx claudepluginhub grexyloco/k.agentsThis skill uses the workspace's default tool permissions.
Adaptiert von: [Aaronontheweb/dotnet-skills](https://github.com/Aaronontheweb/dotnet-skills) (MIT)
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Share bugs, ideas, or general feedback.
Adaptiert von: Aaronontheweb/dotnet-skills (MIT)
| Situation | Pattern |
|---|---|
| I/O-bound (HTTP, DB, File) | async/await |
| Parallel CPU-bound | Parallel.ForEachAsync |
| Producer/Consumer | Channel<T> |
| Rate Limiting | SemaphoreSlim |
| Background Work | IHostedService / BackgroundService |
| Fire-and-Forget (mit Fehlerhandling) | Task.Run + Error-Handler |
// ✅ Korrekt
public async Task<User> GetUserAsync(Guid id, CancellationToken ct = default)
{
return await repository.GetByIdAsync(id, ct);
}
// ❌ Verboten: async void (nur für Event-Handler)
// ❌ Verboten: .Result oder .Wait() (Deadlock-Gefahr)
// ❌ Verboten: Task.Run um async zu wrappen
// Immer als letzten Parameter durchreichen
public async Task<List<User>> SearchAsync(string query, CancellationToken ct = default)
{
ct.ThrowIfCancellationRequested();
return await context.Users
.Where(u => u.Name.Contains(query))
.ToListAsync(ct);
}
// ✅ Parallel statt sequentiell
var (users, orders, stats) = await (
userService.GetAllAsync(ct),
orderService.GetRecentAsync(ct),
statsService.GetDashboardAsync(ct)
).WhenAll();
// Extension für Tuple-Destructuring:
public static class TaskExtensions
{
public static async Task<(T1, T2, T3)> WhenAll<T1, T2, T3>(
this (Task<T1>, Task<T2>, Task<T3>) tasks)
{
await Task.WhenAll(tasks.Item1, tasks.Item2, tasks.Item3);
return (tasks.Item1.Result, tasks.Item2.Result, tasks.Item3.Result);
}
}
var channel = Channel.CreateBounded<WorkItem>(100);
// Producer
async Task ProduceAsync(ChannelWriter<WorkItem> writer)
{
await foreach (var item in GetItemsAsync())
{
await writer.WriteAsync(item);
}
writer.Complete();
}
// Consumer
async Task ConsumeAsync(ChannelReader<WorkItem> reader, CancellationToken ct)
{
await foreach (var item in reader.ReadAllAsync(ct))
{
await ProcessAsync(item, ct);
}
}
private static readonly SemaphoreSlim _semaphore = new(maxConcurrency: 10);
public async Task<Result> ProcessWithLimitAsync(CancellationToken ct)
{
await _semaphore.WaitAsync(ct);
try
{
return await DoExpensiveWorkAsync(ct);
}
finally
{
_semaphore.Release();
}
}
ConcurrentDictionary<K,V> statt Dictionary + LockInterlocked.Increment statt lock { counter++ }ImmutableList<T> / FrozenDictionary<K,V> für Read-Heavy Szenarienlock nur für kurze, synchrone Operationenasync Code innerhalb von lock — verwende SemaphoreSlim