Ereignisse
17. März, 23 Uhr - 21. März, 23 Uhr
Nehmen Sie an der Meetup-Serie teil, um skalierbare KI-Lösungen basierend auf realen Anwendungsfällen mit Mitentwicklern und Experten zu erstellen.
Jetzt registrierenDieser Browser wird nicht mehr unterstützt.
Führen Sie ein Upgrade auf Microsoft Edge aus, um die neuesten Funktionen, Sicherheitsupdates und technischen Support zu nutzen.
Mit Task.WhenAny können Sie mehrere Aufgaben gleichzeitig starten und diese nicht in der Reihenfolge, in der sie gestartet wurden, sondern zu dem Zeitpunkt, zu dem sie abgeschlossen werden, verarbeiten.
Im folgenden Beispiel wird eine Abfrage verwendet, um eine Auflistung von Aufgaben zu erstellen. Jede Aufgabe lädt den Inhalt einer angegebenen Website herunter. In jeder Iteration einer While-Schleife gibt ein erwarteter Aufruf von WhenAny die Aufgabe in der Auflistung von Aufgaben zurück, die ihren Download zuerst beendet. Diese Aufgabe wird aus der Auflistung entfernt und verarbeitet. Die Ausführung der Schleife wird wiederholt, bis die Auflistung keine Aufgaben mehr enthält.
Sie können die Schritte dieses Tutorials mithilfe einer der folgenden Optionen befolgen:
Erstellen Sie eine neue .NET Core-Konsolenanwendung. Sie können mithilfe des dotnet new console-Befehls oder über Visual Studio eine solche Anwendung erstellen.
Öffnen Sie die Datei Program.cs in Ihrem Code-Editor, und ersetzen Sie den vorhandenen Code durch folgenden Code:
using System.Diagnostics;
namespace ProcessTasksAsTheyFinish;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
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"
};
Der HttpClient
ermöglicht das Senden von HTTP-Anforderungen und das Empfangen von HTTP-Antworten. s_urlList
enthält alle URLs, die von der Anwendung verarbeitet werden sollen.
Der Haupteinstiegspunkt in die Konsolenanwendung ist die Main
-Methode. Ersetzen Sie die vorhandene Methode durch Folgendes:
static Task Main() => SumPageSizesAsync();
Die aktualisierte Main
-Methode wird jetzt als Async main-Methode betrachtet, die einen asynchronen Einstiegspunkt in die ausführbare Datei ermöglicht. Sie wird mit einem Aufruf von SumPageSizesAsync
angegeben.
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. Die Schleife endet nach Abschluss aller Aufgaben. Die Methode beginnt mit dem Instanziieren und Starten einer Stopwatch-Klasse. Dann ist eine Abfrage enthalten, die eine Auflistung von Aufgaben erstellt, wenn sie ausgeführt wird. Jeder Aufruf von ProcessUrlAsync
im folgenden Code gibt Task<TResult> zurück, wobei TResult
eine ganze Zahl ist:
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
Aufgrund der verzögerten Ausführung mit LINQ wird Enumerable.ToList aufgerufen, um die einzelnen Aufgaben zu starten.
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
Die while
-Schleife führt die folgenden Schritte für jede Aufgabe in der Auflistung aus:
Es wird ein Aufruf von WhenAny
erwartet, um die erste Aufgabe in der Auflistung zu identifizieren, die ihren Download beendet hat.
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
Entfernt die entsprechende Aufgabe aus der Auflistung.
downloadTasks.Remove(finishedTask);
Erwartet finishedTask
, das durch einen Aufruf von ProcessUrlAsync
zurückgegeben wird. Die Variable finishedTask
ist eine Task<TResult>, wobei TResult
eine ganze Zahl ist. Die Aufgabe ist bereits abgeschlossen, aber es darauf gewartet, dass von ihr die Länge der heruntergeladenen Website abgerufen wird, wie im folgenden Beispiel dargestellt. Wenn für die Aufgabe ein Fehler auftritt, löst await
im Gegensatz zum Lesen der Task<TResult>.Result-Eigenschaft, die die AggregateException
auslösen würde, die erste untergeordnete Ausnahme aus, die in der AggregateException
gespeichert ist.
total += await finishedTask;
Fügen Sie die folgende ProcessUrlAsync
-Methode unterhalb 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 eine beliebige URL verwendet die Methode die client
-Instanz, die bereitgestellt wird, um die Antwort als byte[]
zu erhalten. 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 zu bestätigen, dass die heruntergeladenen Längen nicht immer in der gleichen Reihenfolge angezeigt werden.
Vorsicht
Sie können WhenAny
in einer Schleife gemäß der Beschreibung im Beispiel verwenden, um Problemlösungen zu entwickeln, die nur wenige Aufgaben umfassen. Andere Ansätze sind jedoch effizienter, wenn viele Aufgaben verarbeitet werden müssen. Weitere Informationen und Beispiele finden Sie unter Processing Tasks as they complete (Verarbeitung von Aufgaben nach deren Abschluss).
Der folgende Code besteht aus dem vollständigen 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
Feedback zu .NET
.NET ist ein Open Source-Projekt. Wählen Sie einen Link aus, um Feedback zu geben:
Ereignisse
17. März, 23 Uhr - 21. März, 23 Uhr
Nehmen Sie an der Meetup-Serie teil, um skalierbare KI-Lösungen basierend auf realen Anwendungsfällen mit Mitentwicklern und Experten zu erstellen.
Jetzt registrierenSchulung
Lernpfad
Erstellen verteilter Apps mit .NET Aspire - Training
Erfahren Sie, wie Sie mit .NET Aspire beobachtbare, produktionsreife, verteilte Anwendungen erstellen.
Dokumentation
In diesem Artikel wird die Verwendung der aufgabenbasierten asynchronen Programmierung erläutert. Dabei handelt es sich um einen vereinfachten Ansatz für die asynchrone Programmierung in C#.
Hier erhalten Sie Informationen zu Rückgabetypen, die asynchrone Methoden in C# aufweisen können, und sehen Codebeispiele für die einzelnen Typen.
Abbrechen asynchroner Aufgaben nach einem bestimmten Zeitraum - C#
Hier erfahren Sie, wie Sie den Abbruch aller zugeordneten Aufgaben planen, die nicht innerhalb eines bestimmten Zeitraums abgeschlossen werden.