RegexOptions.Compiled).Matches(src).Cast<Match>().Select(m => m.Groups[1].Value).Where(s => !string.IsNullOrEmpty(s)).Distinct();
Alte URL:
/snippet/klasse-zur-parallelen-verarbeitung-von-tasks/12069
Das Muster, mehrere Tasks parallel auszuführen und auf alle zu warten, ist grundsätzlich weiterhin relevant. Der gezeigte Code spiegelt aber einen älteren Stil ohne moderne Asynchronitäts- und Parallelisierungs-Primitiven wider. Insbesondere wird eine eigene Warteschleife über Task.WhenAny geschrieben, statt vorhandene Bibliotheken und Sprachfeatures zu nutzen. Außerdem fehlen Cancellation-Unterstützung, saubere Fehlerbehandlung und Kontrolle über den Grad der Parallelität.
[b]Analyse nach heutigen Kriterien:[/b]
– [u]Performance[/u]: Die manuelle Schleife über Task.WhenAny mit Entfernen aus einer Liste funktioniert, ist aber unnötig kompliziert und erzeugt zusätzliche List-Allokationen. Task.WhenAll ist für „alle Tasks beenden“ effizienter und semantisch klarer.
– [u]Memory-Allokationen[/u]: Die mutable Task-Liste und wiederholte Remove-Operationen erzeugen unnötige Allokationen und Reallokationen. Moderne APIs kommen ohne diese Struktur aus.
– [u]Korrektheit[/u]: Exceptions einzelner Tasks werden nicht zentral behandelt. Fehler können unbemerkt bleiben oder erst sehr spät sichtbar werden.
– [u]Robustheit[/u]: Es fehlt jegliche Cancellation-Unterstützung. In zeitgemäßen APIs gehört ein CancellationToken zwingend dazu.
– [u]Thread-Safety[/u]: Der Ansatz ist grundsätzlich thread-safe, aber die manuelle Verwaltung der Task-Liste macht den Code fehleranfällig bei Erweiterungen.
– [u]Cloud/Container[/u]: Blockierende Wait- oder Result-Zugriffe sind in Cloud-Workloads problematisch. Async/await ist der erwartete Standard.
– [u]Standard-.NET-Ansatz[/u]: Task.WhenAll, Parallel.ForEachAsync, SemaphoreSlim oder Channels sind heute die bevorzugten Werkzeuge.
[b]Modernisierte Variante (async/await, WhenAll, Cancellation):[/b]
[code]
using System;
using System.Threading;
using System.Threading.Tasks;
public static class ParallelTasks[] taskFactories, CancellationToken cancellationToken = default)
{
public static Task WhenAll(Func
{
if (taskFactories is null) throw new ArgumentNullException(nameof(taskFactories));
var tasks = new Task[taskFactories.Length];
for (int i = 0; i < taskFactories.Length; i++) tasks[i] = taskFactories[i](cancellationToken); return Task.WhenAll(tasks); } } [/code] [b]Warum das heute objektiv besser ist:[/b] - [u]Performance[/u]: Task.WhenAll nutzt die Laufzeit effizienter als manuelle Schleifen mit WhenAny. - [u]Robustheit[/u]: Fehler werden gesammelt propagiert, Cancellation ist sauber integrierbar. - [u]Memory-Allokationen[/u]: Keine dynamischen Listenmanipulationen. - [u]Cloud-Tauglichkeit[/u]: Async/await ohne Blockieren verhindert Threadpool-Starvation. [b]Security-Realitätscheck:[/b] Fehlende Cancellation oder Timeouts können in Server-Szenarien zu Ressourcenerschöpfung führen. Parallele Verarbeitung sollte immer begrenzt und kontrollierbar sein.