Back to "Aufbau einer wiederverwendbaren Ephemeral Execution Library"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

Architecture ASP.NET Async DI Systems Design

Aufbau einer wiederverwendbaren Ephemeral Execution Library

Friday, 12 December 2025

Im **Teil: Feuer und Don-' Ganz schön Vergessen**Wir haben die Theorie hinter der ephemeren Ausführung untersucht.- bounded.MSC2 private, debuggable async Workflows, die gerade genug erinnern, um nützlich zu sein und dann verdunsten.

Dieser Artikel verwandelt dieses Muster in eine wiederverwendbare Bibliothek, die Sie in ein beliebiges .-NET-Projekt fallen lassen können.

NUGET-!!!

Dies ist jetzt im meistlucid-.-Ephemerals Nuget-Paket auch mehr als 20, meistluzid, ., Ephemermuster und ', Atome, MSC3,.

NuGet Lizenz

Quelldateien

Der vollständige Quellcode ist in der meistlucid-.-Toms GitHub-Repository


Vor und nach

Hier ist das, was wir ersetzen '

// ❌ Before: Fire-and-forget black hole
_ = Task.Run(() => ProcessAsync(item));
// No visibility. No debugging. No idea if it worked.

// ❌ Or: Blocking everything
await ProcessAsync(item);  // Hope you like waiting...

Und was bauen wir?

// ✅ After: Trackable, bounded, debuggable
await coordinator.EnqueueAsync(item);

// Instant visibility
Console.WriteLine($"Pending: {coordinator.PendingCount}");
Console.WriteLine($"Active: {coordinator.ActiveCount}");
Console.WriteLine($"Failed: {coordinator.TotalFailed}");

// Full operation history
var snapshot = coordinator.GetSnapshot();
var failures = coordinator.GetFailed();

Gleiche async-Ausführung – .- Vollständige Beobachtbarkeit –.-Keine gespeicherten Benutzerdaten


Schnellstart

Das gängigste Muster registriert einen Koordinator in DI und injiziert es.

// Program.cs
services.AddEphemeralWorkCoordinator<TranslationRequest>(
    async (request, ct) => await TranslateAsync(request, ct),
    new EphemeralOptions { MaxConcurrency = 8 });

// Your service
public class TranslationService(EphemeralWorkCoordinator<TranslationRequest> coordinator)
{
    public async Task TranslateAsync(TranslationRequest request)
    {
        await coordinator.EnqueueAsync(request);
        // Returns immediately - work happens in background
    }

    public object GetStatus() => new
    {
        pending = coordinator.PendingCount,
        active = coordinator.ActiveCount,
        completed = coordinator.TotalCompleted,
        failed = coordinator.TotalFailed
    };
}

Welche Variante brauche ich?

┌─────────────────────────────────────────────────────────────────┐
│                    DECISION TREE                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Processing a collection once?                                  │
│  └─► EphemeralForEachAsync<T> (ParallelEphemeral.cs)            │
│                                                                 │
│  Need a long-lived queue that accepts items over time?          │
│  └─► EphemeralWorkCoordinator<T>                                │
│                                                                 │
│  Need per-entity ordering (user commands, tenant jobs)?         │
│  └─► EphemeralKeyedWorkCoordinator<TKey, T>                     │
│                                                                 │
│  Need to capture results (fingerprints, summaries)?             │
│  └─► EphemeralResultCoordinator<TInput, TResult>                │
│                                                                 │
│  Need multiple coordinators with different configs?             │
│  └─► IEphemeralCoordinatorFactory<T> (like IHttpClientFactory)  │
│                                                                 │
│  Need dynamic concurrency adjustment at runtime?                │
│  └─► Set EnableDynamicConcurrency = true, call SetMaxConcurrency│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Das Konfigurationsobjekt

Von Ephemere Optionen:

public sealed class EphemeralOptions
{
    // Concurrency control
    public int MaxConcurrency { get; init; } = Environment.ProcessorCount;
    public int MaxConcurrencyPerKey { get; init; } = 1;
    public bool EnableDynamicConcurrency { get; init; } = false;

    // Window management
    public int MaxTrackedOperations { get; init; } = 200;
    public TimeSpan? MaxOperationLifetime { get; init; } = TimeSpan.FromMinutes(5);

    // Fair scheduling (keyed coordinator)
    public bool EnableFairScheduling { get; init; } = false;
    public int FairSchedulingThreshold { get; init; } = 10;

    // Signal-reactive processing
    public IReadOnlySet<string>? CancelOnSignals { get; init; }
    public IReadOnlySet<string>? DeferOnSignals { get; init; }
    public int MaxDeferAttempts { get; init; } = 10;
    public TimeSpan DeferCheckInterval { get; init; } = TimeSpan.FromMilliseconds(100);

    // Signal infrastructure
    public SignalSink? Signals { get; init; }
    public SignalConstraints? SignalConstraints { get; init; }
    public Action<SignalEvent>? OnSignal { get; init; }

    // Async signal handling
    public Func<SignalEvent, CancellationToken, Task>? OnSignalAsync { get; init; }
    public int MaxConcurrentSignalHandlers { get; init; } = 4;
    public int MaxQueuedSignals { get; init; } = 1000;

    // Observability
    public Action<IReadOnlyCollection<EphemeralOperationSnapshot>>? OnSample { get; init; }
}

Schlüsselentscheidungen für die Gestaltung

  • Maximale Übereinstimmung Standardwerte für die CPU-Zählung -, sinnvoll für CPU--, gebundene Arbeit ., für IM/, gebundenes Arbeiten ,, erhöhen es .,
  • DynamischeKonkurrenz aktivieren ermöglicht die Einstellung der Laufzeit über SetMaxConcurrency() Ein benutzerdefiniertes Gate anstelle von SemaphoreSlim.
  • AbbrechenOnSignals-/-DeferOnSignale make coordinators signal---reaktive -- sie reagieren auf den Zustand des Umgebungssystems */?Weitere Informationen sind abrufbar unter: http://www.cordis.lu/
  • AufSignal ist synchron, für async-lüfter, --out SignalDispatcher oder AsyncSignalProcessor im Inneren des Handlers
  • Signalbeeinträchtigungen verhindert unendliche Signalschleifen mit Zyklusdetektion und Tiefengrenzwerte

Die Snapshot-Datensätze

Von Schnappschusse ,. ,cs:

public sealed record EphemeralOperationSnapshot(
    long Id,
    DateTimeOffset Started,
    DateTimeOffset? Completed,
    string? Key,
    bool IsFaulted,
    Exception? Error,
    TimeSpan? Duration,
    IReadOnlyList<string>? Signals = null,
    bool IsPinned = false)
{
    public bool HasSignal(string signal) => Signals?.Contains(signal) == true;
}

// For result-capturing coordinators
public sealed record EphemeralOperationSnapshot<TResult>(
    long Id,
    DateTimeOffset Started,
    DateTimeOffset? Completed,
    string? Key,
    bool IsFaulted,
    Exception? Error,
    TimeSpan? Duration,
    TResult? Result,
    bool HasResult,
    IReadOnlyList<string>? Signals = null,
    bool IsPinned = false);

Das ist nur MetadatenBemerken Sie, was -' ist nicht Hier ist :.

  • Keine Nutzlast
  • Keine Eingabedaten
  • Kein Benutzerinhalt

Gerade genug, um zu beantworten, was geschah, wenn, , und tat es funktionierte, ?" und nicht mehr.


Wie dies mit anderen Ansätzen verglichen wird

.-NET gibt Ihnen mehrere Möglichkeiten, parallele Arbeit zu tun. .. Hier ist '. Wie die Ephemere Bibliothek vergleicht.

Parallel für jedesAsync (.-NET

await Parallel.ForEachAsync(items,
    new ParallelOptions { MaxDegreeOfParallelism = 4 },
    async (item, ct) => await ProcessAsync(item, ct));

Das Beste fürEinfache parallele Verarbeitung von Sammlungen, in denen Sie nicht benötigen Sichtbarkeit

Was ihm fehlt:

  • Keine Betriebsverfolgung
  • Keine sequentielle Ausführung per--.
  • Keine Sichtbarkeit in was-'

Ephemeral anwenden, wennSie benötigen Debugging ,/ Beobachtbarkeit , , , per , - key Bestellung , oder Signal , - , reaktive Verarbeitung ,

TPL-Datenfluss

var block = new ActionBlock<T>(
    async item => await ProcessAsync(item),
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 4 });

foreach (var item in items)
    block.Post(item);

block.Complete();
await block.Completion;

Das Beste fürKomplexe Datenflusspipelines mit Verzweigung – , – Zusammenführen von , – Batching – MKK3 –

Was es gut macht:

  • Reiche Pipeline-Zusammensetzung Verkettungsblöcke zusammen
  • Konstruiert in Batching, ,, transformierend in ,, Rundfunk
  • Gebundene Kapazität mit rückseitigem Druck

TPL Dataflow verwenden, wennSie benötigen komplexe Pipeline-Topologien, bedingtes Routing, ).

Ephemeral anwenden, wennSie benötigen Operations-Tracking - einfachere API - , oder Signal - reaktive Koordination - .

System ,. , Threading , . , Channels

var channel = Channel.CreateBounded<T>(100);

// Producer
foreach (var item in items)
    await channel.Writer.WriteAsync(item);
channel.Writer.Complete();

// Consumer (multiple workers)
var workers = Enumerable.Range(0, 4).Select(async _ =>
{
    await foreach (var item in channel.Reader.ReadAllAsync())
        await ProcessAsync(item);
});
await Task.WhenAll(workers);

Das Beste fürVerbrauchermuster, bei denen Sie beide Seiten kontrollieren

Was es gut macht:

  • Ausgezeichnete Leistung
  • Rückwärtsdruck des Drucks über geschränkte Kanäle
  • Trennung von Erzeugern und Verbrauchern

Kanäle verwenden, wennSie bauen benutzerdefinierte Infrastruktur und benötigen maximale Kontrolle.

Ephemeral anwenden, wennSie möchten Betriebsverfolgung und Beobachtbarkeit ohne die Kesselplatte?

Polly

var policy = Policy
    .Handle<HttpRequestException>()
    .WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));

await policy.ExecuteAsync(() => ProcessAsync(item));

Das Beste fürRessilienz-Politiken für einzelne Operationen

Polly verwenden, wennSie brauchen Widerstandsfähigkeit um individuelle Anrufe.

Ephemeral anwenden, wennSie müssen über viele Operationen mit Umgebungsbewusstsein koordiniert werden.

Kombiniere sieVerwenden Sie Polly im Inneren Ihres Ephemeren Arbeitskörpers für die Per---Belastbarkeit

MasseTransit-Massentransit-NServiceBus

Das Beste fürVerteilte Messaging-Dienste mit dauerhaften Warteschlangen

Nachrichtenbusse verwenden, wennArbeiten müssen Prozess-Neustarts überleben , , spannen mehrere Dienste , oder erfordern eine garantierte Lieferung ,. ,

Ephemeral anwenden, wennDie Arbeit ist in ,-' Prozess , ,' doesns , , und Sie wollen leichte Beobachtbarkeit , .'

Vergleichstabelle

-Annäherung - Gebundene , -Nachverfolgung , -Per---Key-Signale - Selbstreinigung , --Reinigung
Parallel.ForEachAsync ✅ ❌T ❌X x❌ N-/1-m- h-m
-TPL-Datenfluss -✅-M -❌- -MSC6- -MSSK8-MSZ9-MS K10-MSD11- Hoch
-Kanale , , ✅ , , ❌ , - ,-❌ , – , ❌ ," , ❌, Medium ,
, Polly, , MSC2, MCSK3, MKSK4, M , MNSK6, MSSK7, ❌, N/, A , Low, MSV12
Hintergrunddienstleistungen , , ❌ , , ❌ , , -❌ ,- , –❌ , ❌ ," , Medium , "
-MasseTransit/-NServiceBus -✅- -M✅-MSC6-✅-MSSK8-MSZ9-MS K10-MS-❌-MSG12-Hoch
Ephemere Bibliothek ✅ ✅T ✅- ✅X✅8- 1-MSC11

EphemeralFürjedenAsync-:-Die Eine---Hot-Version

Von ParallelEphemeral-.-cs:

// Simple parallel processing with tracking
await items.EphemeralForEachAsync(
    async (item, ct) => await ProcessAsync(item, ct),
    new EphemeralOptions { MaxConcurrency = 8 });

// With keyed execution (per-user sequential)
await commands.EphemeralForEachAsync(
    cmd => cmd.UserId,  // Key selector
    async (cmd, ct) => await ExecuteCommandAsync(cmd, ct),
    new EphemeralOptions
    {
        MaxConcurrency = 32,
        MaxConcurrencyPerKey = 1  // Sequential per user
    });

Warum schlüsselfertige Pipelines wichtig sind

Stellen Sie sich vor, Benutzerbefehle zu verarbeiten.

  • Benutzer A sendet Befehle
  • Benutzer B sendet Befehle

Ohne Keying könnten diese als : ausführen.

Mit MaxConcurrencyPerKey = 1:

  • Benutzer-Befehle, die in der Reihenfolge ausgeführt werden
  • Benutzer-Befehle, die in der Reihenfolge ausgeführt werden
  • Aber A und B können parallel laufen

Das ist Per--'entity sequentiell ,,' global parallel Für Systeme, bei denen die Ordnung innerhalb einer Einheit von Bedeutung ist, kritisch


Der Arbeitskoordinator„:" Eine lange, lebendige Warteschlange

Von Ephemere ArbeitKoordinator:

await using var coordinator = new EphemeralWorkCoordinator<TranslationRequest>(
    async (request, ct) => await TranslateAsync(request, ct),
    new EphemeralOptions
    {
        MaxConcurrency = 8,
        MaxTrackedOperations = 500,
        EnableDynamicConcurrency = true  // Allow runtime adjustment
    });

// Enqueue items over time
await coordinator.EnqueueAsync(new TranslationRequest("Hello", "es"));

// Check status anytime
Console.WriteLine($"Pending: {coordinator.PendingCount}");
Console.WriteLine($"Active: {coordinator.ActiveCount}");

// Get snapshots
var snapshot = coordinator.GetSnapshot();
var running = coordinator.GetRunning();
var failed = coordinator.GetFailed();
var completed = coordinator.GetCompleted();

// Control flow
coordinator.Pause();   // Stop pulling new work
coordinator.Resume();  // Continue

// Adjust concurrency at runtime (requires EnableDynamicConcurrency)
coordinator.SetMaxConcurrency(16);

// Pin important operations to survive eviction
coordinator.Pin(operationId);
coordinator.Unpin(operationId);
coordinator.Evict(operationId);

// When done
coordinator.Complete();
await coordinator.DrainAsync();

Kontinuierliche Streams mit IAsyncEnumerable

await using var coordinator = EphemeralWorkCoordinator<Message>.FromAsyncEnumerable(
    messageStream,  // IAsyncEnumerable<Message>
    async (msg, ct) => await ProcessMessageAsync(msg, ct),
    new EphemeralOptions { MaxConcurrency = 16 });

await coordinator.DrainAsync();

Die Keyed-Koordinator-:-Per---Entity Pipelines

Von EphemeralKeyedWorkKoordinator:

await using var coordinator = new EphemeralKeyedWorkCoordinator<string, Command>(
    cmd => cmd.UserId,  // Key selector
    async (cmd, ct) => await ExecuteCommandAsync(cmd, ct),
    new EphemeralOptions
    {
        MaxConcurrency = 32,
        MaxConcurrencyPerKey = 1,      // Per-user sequential
        EnableFairScheduling = true,   // Prevent hot user starvation
        FairSchedulingThreshold = 10   // Reject if user has 10+ pending
    });

// TryEnqueue returns false if fair scheduling rejects
if (!coordinator.TryEnqueue(hotUserCommand))
{
    await DeferCommandAsync(hotUserCommand);
}

// Per-key visibility
var pendingForUser = coordinator.GetPendingCountForKey("user-123");
var opsForUser = coordinator.GetSnapshotForKey("user-123");

Ergebnis: --Capturing-Koordinatoren

Von EphemeralResultKoordinator:

await using var coordinator = new EphemeralResultCoordinator<SessionInput, SessionResult>(
    async (input, ct) =>
    {
        var fingerprint = await ComputeFingerprintAsync(input.Events, ct);
        return new SessionResult(fingerprint, input.Events.Length);
    },
    new EphemeralOptions { MaxConcurrency = 16 });

await coordinator.EnqueueAsync(session);
coordinator.Complete();
await coordinator.DrainAsync();

// Get just the results (no metadata)
var results = coordinator.GetResults();

// Get snapshots with results + metadata
var snapshots = coordinator.GetSnapshot();

// Get base snapshots without results (privacy-safe)
var baseSnapshots = coordinator.GetBaseSnapshot();

// Filter by success/failure
var successful = coordinator.GetSuccessful();
var failed = coordinator.GetFailed();

Kontrolle der Übereinstimmung

Von ÜbereinstimmungGates-.-cs:

Die Bibliothek bietet zwei Concurrency-Kontrollmechanismen.

BehobenConcurrencyGate

  • Gesichert von SemaphoreSlim
  • Optimale Hot---Path-Leistung
  • Zur Laufzeit nicht justierbar

Einstellbares ConcurrenzGate

  • Benutzerdefinierte Implementierung mit Queue<WaiterEntry>
  • Unterstützungen UpdateLimit() zur Laufzeit
  • Aktiviert über EnableDynamicConcurrency = true
// Dynamic concurrency adjustment
var coordinator = new EphemeralWorkCoordinator<T>(body,
    new EphemeralOptions
    {
        MaxConcurrency = 4,
        EnableDynamicConcurrency = true
    });

// Later, based on system load:
coordinator.SetMaxConcurrency(16);  // Scale up
coordinator.SetMaxConcurrency(2);   // Scale down

Die Werksmuster-Koordinatoren

Von Abhängig keitInjektion:

Wie IHttpClientFactorySie können benannte Konfigurationen registrieren.

// Registration
services.AddEphemeralWorkCoordinator<TranslationRequest>("fast",
    async (request, ct) => await FastTranslateAsync(request, ct),
    new EphemeralOptions { MaxConcurrency = 32 });

services.AddEphemeralWorkCoordinator<TranslationRequest>("accurate",
    async (request, ct) => await AccurateTranslateAsync(request, ct),
    new EphemeralOptions { MaxConcurrency = 4 });

// Usage
public class TranslationService(IEphemeralCoordinatorFactory<TranslationRequest> factory)
{
    private readonly EphemeralWorkCoordinator<TranslationRequest> _fast =
        factory.CreateCoordinator("fast");
    private readonly EphemeralWorkCoordinator<TranslationRequest> _accurate =
        factory.CreateCoordinator("accurate");
}

Betriebsgarantien

  1. Der gleiche Name ist dieselbe Instanz. Anrufen CreateCoordinator("fast") zweimal gibt den gleichen Koordinator zurück
  2. Verschiedene Namen, verschiedene Instanzen - "fast" und "accurate" separate Koordinatoren erhalten
  3. Faule Schöpfung Die Koordinatoren werden nur erstellt, wenn sie zuerst angefordert werden.
  4. Validierung der Konfiguration Die Anforderung eines nicht registrierten Namens wirft einen hilfreichen Fehler auf

Signalabfrage-API

Alle Koordinatoren stellen optimierte Signalabfragemethoden zur Verfügung

// Get all signals
var signals = coordinator.GetSignals();

// Filter by key (zero-allocation)
var userSignals = coordinator.GetSignalsByKey("user-123");

// Filter by time range
var recentSignals = coordinator.GetSignalsSince(DateTimeOffset.UtcNow.AddMinutes(-5));
var rangeSignals = coordinator.GetSignalsByTimeRange(from, to);

// Filter by signal name or pattern
var rateSignals = coordinator.GetSignalsByName("rate-limit");
var httpSignals = coordinator.GetSignalsByPattern("http.*");

// Check existence (short-circuits on first match)
if (coordinator.HasSignal("rate-limit"))
    await ThrottleAsync();

if (coordinator.HasSignalMatching("error.*"))
    await AlertAsync();

// Count signals efficiently (no allocation)
var totalSignals = coordinator.CountSignals();
var errorCount = coordinator.CountSignals("error");
var httpCount = coordinator.CountSignalsMatching("http.*");

Produktionsoptimierungen

Schnelle ID-Erstellung

Von EphemeralIdGenerator-.-cs:

internal static class EphemeralIdGenerator
{
    private static long _counter;
    private static readonly long _processStart = Environment.TickCount64;
    private static readonly int _processId = Environment.ProcessId;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static long NextId()
    {
        var counter = Interlocked.Increment(ref _counter);

        // Combine counter with process-unique seed
        Span<byte> buffer = stackalloc byte[24];
        BitConverter.TryWriteBytes(buffer, _processStart);
        BitConverter.TryWriteBytes(buffer.Slice(8), _processId);
        BitConverter.TryWriteBytes(buffer.Slice(16), counter);

        return unchecked((long)XxHash64.HashToUInt64(buffer));
    }
}
  • Verteilungsfrei Verwendungen stackalloc)
  • Thread---sicher Verwendungen Interlocked.Increment)
  • Einzigartig über alle Prozesse hinweg Einschließlich Prozess ID.).
  • Nicht-- sequenzielle Der Zähler wird von (hash diffundiert

Speicher - Sichere Langzeit-- - Lived Operation

Die Koordinatoren speichern Task Referenzen - gerade Zähler , :

private int _activeTaskCount;
private readonly TaskCompletionSource _drainTcs;

// In ExecuteItemAsync:
finally
{
    // Signal drain when last task completes AND channel iteration is done
    if (Interlocked.Decrement(ref _activeTaskCount) == 0 &&
        Volatile.Read(ref _channelIterationComplete))
    {
        _drainTcs.TrySetResult();
    }
}

Bereinigung der Schlüsselsperre

Der Keyed-Koordinator säubert automatisch den Leerlauf per.-.key Semaphores.

private sealed class KeyLock(SemaphoreSlim gate, int maxCount)
{
    public SemaphoreSlim Gate { get; } = gate;
    public int MaxCount { get; } = maxCount;
    public long LastUsedTicks = Environment.TickCount64;
}

// Cleanup runs periodically, removes locks idle > 60 seconds

Vollständiges Beispiel

// Program.cs
var builder = WebApplication.CreateBuilder(args);

// Named coordinators
builder.Services.AddEphemeralWorkCoordinator<TranslationRequest>("fast",
    async (req, ct) => await FastTranslateAsync(req, ct),
    new EphemeralOptions { MaxConcurrency = 16 });

// Keyed coordinator for per-user commands
builder.Services.AddEphemeralKeyedWorkCoordinator<string, UserCommand>("commands",
    cmd => cmd.UserId,
    sp =>
    {
        var handler = sp.GetRequiredService<ICommandHandler>();
        return async (cmd, ct) => await handler.HandleAsync(cmd, ct);
    },
    new EphemeralOptions
    {
        MaxConcurrency = 32,
        MaxConcurrencyPerKey = 1,
        EnableFairScheduling = true,
        CancelOnSignals = new HashSet<string> { "system-overload" }
    });

var app = builder.Build();
// Controller
[ApiController]
[Route("api")]
public class WorkController : ControllerBase
{
    private readonly EphemeralWorkCoordinator<TranslationRequest> _translator;
    private readonly EphemeralKeyedWorkCoordinator<string, UserCommand> _commands;

    public WorkController(
        IEphemeralCoordinatorFactory<TranslationRequest> translationFactory,
        IEphemeralKeyedCoordinatorFactory<string, UserCommand> commandFactory)
    {
        _translator = translationFactory.CreateCoordinator("fast");
        _commands = commandFactory.CreateCoordinator("commands");
    }

    [HttpPost("translate")]
    public async Task<IActionResult> Translate([FromBody] TranslationRequest request)
    {
        await _translator.EnqueueAsync(request);
        return Ok(new { pending = _translator.PendingCount });
    }

    [HttpPost("command")]
    public IActionResult SubmitCommand([FromBody] UserCommand command)
    {
        if (!_commands.TryEnqueue(command))
            return StatusCode(429, "Too many pending commands for this user");
        return Ok();
    }

    [HttpGet("status")]
    public IActionResult GetStatus() => Ok(new
    {
        translator = new
        {
            pending = _translator.PendingCount,
            active = _translator.ActiveCount,
            completed = _translator.TotalCompleted,
            failed = _translator.TotalFailed,
            hasRateLimit = _translator.HasSignal("rate-limit")
        },
        commands = new
        {
            pending = _commands.PendingCount,
            active = _commands.ActiveCount,
            errorCount = _commands.CountSignalsMatching("error.*")
        }
    });
}

Schlußfolgerung

Wir bauten eine komplette ephemere Ausführungsbibliothek mit

  1. EphemeralForEachAsync Parallele Verarbeitung mit Tracking
  2. EphemeralWorkCoordinator Dauerhafte Warteschlangen
  3. EphemeralKeyedWorkCoordinator PERSÖNLICHE ZEITSCHRIFT
  4. EphemeralResultCoordinator Ergebnis: -
  5. Fabrikmuster Benannte Konfigurationen wie IHttpClientFactory
  6. Dynamische Übereinstimmung Laufzeitanpassung der Parallelität
  7. Signalinfrastruktur Signal-Emission und Abfragen

Das Muster sitzt in einem süßen Fleck.

  • Beobachtbarer als Parallel.ForEachAsync
  • Einfacher als TPL-Datenfluss
  • Mehr integriert als rohe Kanäle
  • Datenschutz - Sicherheit durch Design

Feuer, ... und Don-'nicht ganz vergessen


Verknüpfungen

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.