Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Mithilfe von Task.WhenAny" können Sie mehrere Aufgaben gleichzeitig starten und sie einzeln verarbeiten, da sie abgeschlossen sind, anstatt sie in der Reihenfolge zu verarbeiten, in der sie gestartet werden.
Im folgenden Beispiel wird eine Abfrage verwendet, um eine Sammlung von Aufgaben zu erstellen. Jede Aufgabe lädt den Inhalt einer angegebenen Website herunter. Bei jeder Iteration einer While-Schleife gibt ein erwarteter Aufruf WhenAny die Aufgabe in der Sammlung von Aufgaben zurück, die den Download zuerst abgeschlossen haben. Diese Aufgabe wird aus der Sammlung entfernt und verarbeitet. Die Schleife wird wiederholt, bis die Auflistung keine weiteren Aufgaben enthält.
Voraussetzungen
Sie können diesem Lernprogramm folgen, indem Sie eine der folgenden Optionen verwenden:
- Visual Studio 2022 mit installierter Workload .NET-Desktopentwicklung. Das .NET SDK wird automatisch installiert, wenn Sie diese Workload auswählen.
- Das .NET SDK mit einem Code-Editor Ihrer Wahl, z. B. Visual Studio Code.
Beispielanwendung erstellen
Erstellen Sie eine neue .NET Core-Konsolenanwendung. Sie können einen erstellen, indem Sie den neuen Konsolenbefehl dotnet oder visual Studio verwenden.
Öffnen Sie die Program.cs Datei im Code-Editor, und ersetzen Sie den vorhandenen Code durch diesen Code:
using System.Diagnostics;
namespace ProcessTasksAsTheyFinish;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
Hinzufügen von Feldern
Fügen Sie in der Program Klassendefinition die folgenden beiden Felder hinzu:
static readonly HttpClient s_client = new HttpClient
{
MaxResponseContentBufferSize = 1_000_000
};
static readonly IEnumerable<string> s_urlList = new string[]
{
"https://learn.microsoft.com",
"https://learn.microsoft.com/aspnet/core",
"https://learn.microsoft.com/azure",
"https://learn.microsoft.com/azure/devops",
"https://learn.microsoft.com/dotnet",
"https://learn.microsoft.com/dynamics365",
"https://learn.microsoft.com/education",
"https://learn.microsoft.com/enterprise-mobility-security",
"https://learn.microsoft.com/gaming",
"https://learn.microsoft.com/graph",
"https://learn.microsoft.com/microsoft-365",
"https://learn.microsoft.com/office",
"https://learn.microsoft.com/powershell",
"https://learn.microsoft.com/sql",
"https://learn.microsoft.com/surface",
"https://learn.microsoft.com/system-center",
"https://learn.microsoft.com/visualstudio",
"https://learn.microsoft.com/windows",
"https://learn.microsoft.com/maui"
};
Dies HttpClient macht die Möglichkeit zum Senden von HTTP-Anforderungen und zum Empfangen von HTTP-Antworten verfügbar. Enthält s_urlList alle URLs, die von der Anwendung verarbeitet werden sollen.
Aktualisieren des Einstiegspunkts der Anwendung
Der Haupteinstiegspunkt in die Konsolenanwendung ist die Main Methode. Ersetzen Sie die vorhandene Methode durch Folgendes:
static Task Main() => SumPageSizesAsync();
Die aktualisierte Main Methode gilt nun als Asynchroner Haupt, der einen asynchronen Einstiegspunkt in die ausführbare Datei ermöglicht. Sie wird als Aufruf ausgedrückt SumPageSizesAsync.
Erstellen der Methode für asynchrone Seitengrößen für die Summe
Fügen Sie unter der Main Methode die SumPageSizesAsync Methode hinzu:
static async Task SumPageSizesAsync()
{
var stopwatch = Stopwatch.StartNew();
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
int total = 0;
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
total += await finishedTask;
}
stopwatch.Stop();
Console.WriteLine($"\nTotal bytes returned: {total:#,#}");
Console.WriteLine($"Elapsed time: {stopwatch.Elapsed}\n");
}
Die while Schleife entfernt eine der Aufgaben in jeder Iteration. Nach Abschluss jeder Aufgabe endet die Schleife. Die Methode beginnt mit dem Instanziieren und Starten eines Stopwatch. Anschließend wird eine Abfrage eingeschlossen, die beim Ausführen eine Sammlung von Aufgaben erstellt. Jeder Aufruf ProcessUrlAsync im folgenden Code gibt eine Task<TResult>ganze Zahl zurück, wobei TResult es sich um eine ganze Zahl handelt:
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
Aufgrund der verzögerten Ausführung mit dem LINQ rufen Enumerable.ToList Sie auf, um jede Aufgabe zu starten.
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
Die while Schleife führt die folgenden Schritte für jeden Vorgang in der Auflistung aus:
Wartet auf einen Aufruf, um die erste Aufgabe in der Sammlung zu
WhenAnyidentifizieren, die den Download abgeschlossen hat.Task<int> finishedTask = await Task.WhenAny(downloadTasks);Entfernt diese Aufgabe aus der Auflistung.
downloadTasks.Remove(finishedTask);Awaits
finishedTask, which is returned by a call toProcessUrlAsync. DiefinishedTaskVariable ist eine Task<TResult> Stelle, anTResultder es sich um eine ganze Zahl handelt. Die Aufgabe ist bereits abgeschlossen, aber Sie warten darauf, die Länge der heruntergeladenen Website abzurufen, wie im folgenden Beispiel gezeigt. Wenn die Aufgabe fehlerhaft ist, löst sie die erste untergeordnete Ausnahme aus,awaitdie im Gegensatz zum Lesen der Task<TResult>.Result Eigenschaft gespeichert ist, wodurch dieAggregateExceptionAggregateException.total += await finishedTask;
Hinzufügen einer Prozessmethode
Fügen Sie die folgende ProcessUrlAsync Methode unter der SumPageSizesAsync Methode hinzu:
static async Task<int> ProcessUrlAsync(string url, HttpClient client)
{
byte[] content = await client.GetByteArrayAsync(url);
Console.WriteLine($"{url,-60} {content.Length,10:#,#}");
return content.Length;
}
Für jede angegebene URL verwendet die Methode die client bereitgestellte Instanz, um die Antwort als eine byte[]. Die Länge wird zurückgegeben, nachdem die URL und die Länge in die Konsole geschrieben wurden.
Führen Sie das Programm mehrmals aus, um sicherzustellen, dass die heruntergeladenen Längen nicht immer in derselben Reihenfolge angezeigt werden.
Vorsicht
Sie können in einer Schleife verwenden WhenAny , wie im Beispiel beschrieben, um Probleme zu lösen, die eine kleine Anzahl von Aufgaben umfassen. Andere Ansätze sind jedoch effizienter, wenn Sie über eine große Anzahl von Aufgaben verfügen, die verarbeitet werden sollen. Weitere Informationen und Beispiele finden Sie unter "Verarbeitungsaufgaben während der Ausführung".
Vereinfachen des Ansatzes mithilfe von Task.WhenEach
Die while in SumPageSizesAsync der Methode implementierte Schleife kann mit der in .NET 9 eingeführten neuen Task.WhenEach Methode vereinfacht werden, indem sie sie in await foreach Schleife aufruft.
Ersetzen Sie die zuvor implementierte while Schleife:
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
total += await finishedTask;
}
mit dem vereinfachten await foreach:
await foreach (Task<int> t in Task.WhenEach(downloadTasks))
{
total += await t;
}
Mit diesem neuen Ansatz kann nicht mehr wiederholt aufgerufen werden, um einen Vorgang manuell aufzurufen Task.WhenAny und den Vorgang zu entfernen, der abgeschlossen ist, da Task.WhenEach der Vorgang in einer Reihenfolge seiner Fertigstellung durchlaufen wird.
Vollständiges Beispiel
Der folgende Code ist der vollständige Text der Program.cs-Datei für das Beispiel.
using System.Diagnostics;
HttpClient s_client = new()
{
MaxResponseContentBufferSize = 1_000_000
};
IEnumerable<string> s_urlList = new string[]
{
"https://learn.microsoft.com",
"https://learn.microsoft.com/aspnet/core",
"https://learn.microsoft.com/azure",
"https://learn.microsoft.com/azure/devops",
"https://learn.microsoft.com/dotnet",
"https://learn.microsoft.com/dynamics365",
"https://learn.microsoft.com/education",
"https://learn.microsoft.com/enterprise-mobility-security",
"https://learn.microsoft.com/gaming",
"https://learn.microsoft.com/graph",
"https://learn.microsoft.com/microsoft-365",
"https://learn.microsoft.com/office",
"https://learn.microsoft.com/powershell",
"https://learn.microsoft.com/sql",
"https://learn.microsoft.com/surface",
"https://learn.microsoft.com/system-center",
"https://learn.microsoft.com/visualstudio",
"https://learn.microsoft.com/windows",
"https://learn.microsoft.com/maui"
};
await SumPageSizesAsync();
async Task SumPageSizesAsync()
{
var stopwatch = Stopwatch.StartNew();
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
int total = 0;
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
total += await finishedTask;
}
stopwatch.Stop();
Console.WriteLine($"\nTotal bytes returned: {total:#,#}");
Console.WriteLine($"Elapsed time: {stopwatch.Elapsed}\n");
}
static async Task<int> ProcessUrlAsync(string url, HttpClient client)
{
byte[] content = await client.GetByteArrayAsync(url);
Console.WriteLine($"{url,-60} {content.Length,10:#,#}");
return content.Length;
}
// Example output:
// https://learn.microsoft.com 132,517
// https://learn.microsoft.com/powershell 57,375
// https://learn.microsoft.com/gaming 33,549
// https://learn.microsoft.com/aspnet/core 88,714
// https://learn.microsoft.com/surface 39,840
// https://learn.microsoft.com/enterprise-mobility-security 30,903
// https://learn.microsoft.com/microsoft-365 67,867
// https://learn.microsoft.com/windows 26,816
// https://learn.microsoft.com/maui 57,958
// https://learn.microsoft.com/dotnet 78,706
// https://learn.microsoft.com/graph 48,277
// https://learn.microsoft.com/dynamics365 49,042
// https://learn.microsoft.com/office 67,867
// https://learn.microsoft.com/system-center 42,887
// https://learn.microsoft.com/education 38,636
// https://learn.microsoft.com/azure 421,663
// https://learn.microsoft.com/visualstudio 30,925
// https://learn.microsoft.com/sql 54,608
// https://learn.microsoft.com/azure/devops 86,034
// Total bytes returned: 1,454,184
// Elapsed time: 00:00:01.1290403