Abbrechen einer Aufgabenliste
Sie können eine asynchrone Konsolenanwendung abbrechen, wenn Sie nicht auf den Abschluss ihrer Ausführung warten möchten. Anhand des in diesem Artikel veranschaulichten Beispiels können Sie einen Abbruch zu einer Anwendung hinzufügen, die die Inhalte von einer Liste von Websites herunterlädt. Sie können viele Aufgaben abbrechen, indem Sie die CancellationTokenSource-Instanz jeder Aufgabe zuordnen. Wenn Sie die EINGABETASTE drücken, brechen Sie alle Aufgaben ab, die noch nicht abgeschlossen wurden.
In diesem Lernprogramm wird Folgendes behandelt:
- Erstellen einer .NET-Konsolenanwendung
- Schreiben einer asynchronen Anwendung, die einen Abbruch unterstützt
- Veranschaulichung der Signalisierung eines Abbruchs
Voraussetzungen
Für dieses Lernprogramm ist Folgendes erforderlich:
- .NET SDK 5 oder höher
- Integrierte Entwicklungsumgebung (Integrated Development Environment, IDE)
Erstellen einer Beispielanwendung
Erstellen Sie eine neue .NET Core-Konsolenanwendung. Sie können eine solche Anwendung mithilfe des Befehls dotnet new console
oder über Visual Studio erstellen. Öffnen Sie die Program.cs-Datei in Ihrem bevorzugten Code-Editor.
Ersetzen von using-Anweisungen
Ersetzen Sie die vorhandenen using-Anweisungen durch diese Deklarationen:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
Hinzufügen von Feldern
Fügen Sie die folgenden drei Felder in der Program
-Klassendefinition hinzu:
static readonly CancellationTokenSource s_cts = new CancellationTokenSource();
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"
};
CancellationTokenSource wird dazu verwendet, einen angeforderten Abbruch für CancellationToken zu signalisieren. HttpClient
ermöglicht das Senden und Empfangen von HTTP-Antworten. s_urlList
enthält 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 async Task Main()
{
Console.WriteLine("Application started.");
Console.WriteLine("Press the ENTER key to cancel...\n");
Task cancelTask = Task.Run(() =>
{
while (Console.ReadKey().Key != ConsoleKey.Enter)
{
Console.WriteLine("Press the ENTER key to cancel...");
}
Console.WriteLine("\nENTER key pressed: cancelling downloads.\n");
s_cts.Cancel();
});
Task sumPageSizesTask = SumPageSizesAsync();
Task finishedTask = await Task.WhenAny(new[] { cancelTask, sumPageSizesTask });
if (finishedTask == cancelTask)
{
// wait for the cancellation to take place:
try
{
await sumPageSizesTask;
Console.WriteLine("Download task completed before cancel request was processed.");
}
catch (TaskCanceledException)
{
Console.WriteLine("Download task has been cancelled.");
}
}
Console.WriteLine("Application ending.");
}
Die aktualisierte Main
-Methode wird jetzt als Async main-Methode betrachtet, die einen asynchronen Einstiegspunkt in die ausführbare Datei ermöglicht. Sie schreibt einige Anweisungsnachrichten an die Konsole und deklariert dann eine Task-Instanz namens cancelTask
, die Tastatureingaben in die Konsole liest. Wenn die EINGABETASTE gedrückt wird, wird ein Aufruf an CancellationTokenSource.Cancel() ausgeführt. Dies signalisiert den Abbruch. Dann wird eine sumPageSizesTask
-Variable der SumPageSizesAsync
-Methode zugewiesen. Beide Aufgaben werden dann an Task.WhenAny(Task[]) übergeben, was fortgesetzt wird, wenn eine der beiden Aufgaben abgeschlossen wird.
Der nächste Codeblock stellt sicher, dass die Anwendung erst beendet wird, wenn der Abbruch verarbeitet wurde. Wenn die erste auszuführende Aufgabe cancelTask
ist, wird auf sumPageSizeTask
gewartet. Wenn ein Abbruch durchgeführt und gewartet wird, wird System.Threading.Tasks.TaskCanceledException ausgelöst. Der Block fängt diese Ausnahme ab und gibt eine Nachricht aus.
Erstellen der SumPageSizesAsync-Methode
Fügen Sie unter der Main
-Methode die SumPageSizesAsync
-Methode hinzu:
static async Task SumPageSizesAsync()
{
var stopwatch = Stopwatch.StartNew();
int total = 0;
foreach (string url in s_urlList)
{
int contentLength = await ProcessUrlAsync(url, s_client, s_cts.Token);
total += contentLength;
}
stopwatch.Stop();
Console.WriteLine($"\nTotal bytes returned: {total:#,#}");
Console.WriteLine($"Elapsed time: {stopwatch.Elapsed}\n");
}
Die Methode beginnt mit dem Instanziieren und Starten einer Stopwatch-Klasse. Anschließend durchläuft sie alle URLs in s_urlList
und ruft ProcessUrlAsync
auf. Mit jeder Iteration wird s_cts.Token
an die ProcessUrlAsync
-Methode übergeben, und der Code gibt Task<TResult> zurück, wobei TResult
ein Integer ist:
int total = 0;
foreach (string url in s_urlList)
{
int contentLength = await ProcessUrlAsync(url, s_client, s_cts.Token);
total += contentLength;
}
Hinzufügen der Prozessmethode
Fügen Sie die folgende ProcessUrlAsync
-Methode unterhalb der SumPageSizesAsync
-Methode hinzu:
static async Task<int> ProcessUrlAsync(string url, HttpClient client, CancellationToken token)
{
HttpResponseMessage response = await client.GetAsync(url, token);
byte[] content = await response.Content.ReadAsByteArrayAsync(token);
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 CancellationToken-Instanz wird an die Methoden HttpClient.GetAsync(String, CancellationToken) und HttpContent.ReadAsByteArrayAsync() übergeben. token
wird dazu verwendet, den angeforderten Abbruch zu registrieren. Die Länge wird zurückgegeben, nachdem die URL und die Länge in die Konsole geschrieben wurden.
Ausgabe der Beispielanwendung
Application started.
Press the ENTER key to cancel...
https://learn.microsoft.com 37,357
https://learn.microsoft.com/aspnet/core 85,589
https://learn.microsoft.com/azure 398,939
https://learn.microsoft.com/azure/devops 73,663
https://learn.microsoft.com/dotnet 67,452
https://learn.microsoft.com/dynamics365 48,582
https://learn.microsoft.com/education 22,924
ENTER key pressed: cancelling downloads.
Application ending.
Vollständiges Beispiel
Der folgende Code besteht aus dem vollständigen Text der Program.cs-Datei für das Beispiel.
using System.Diagnostics;
class Program
{
static readonly CancellationTokenSource s_cts = new CancellationTokenSource();
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"
};
static async Task Main()
{
Console.WriteLine("Application started.");
Console.WriteLine("Press the ENTER key to cancel...\n");
Task cancelTask = Task.Run(() =>
{
while (Console.ReadKey().Key != ConsoleKey.Enter)
{
Console.WriteLine("Press the ENTER key to cancel...");
}
Console.WriteLine("\nENTER key pressed: cancelling downloads.\n");
s_cts.Cancel();
});
Task sumPageSizesTask = SumPageSizesAsync();
Task finishedTask = await Task.WhenAny(new[] { cancelTask, sumPageSizesTask });
if (finishedTask == cancelTask)
{
// wait for the cancellation to take place:
try
{
await sumPageSizesTask;
Console.WriteLine("Download task completed before cancel request was processed.");
}
catch (OperationCanceledException)
{
Console.WriteLine("Download task has been cancelled.");
}
}
Console.WriteLine("Application ending.");
}
static async Task SumPageSizesAsync()
{
var stopwatch = Stopwatch.StartNew();
int total = 0;
foreach (string url in s_urlList)
{
int contentLength = await ProcessUrlAsync(url, s_client, s_cts.Token);
total += contentLength;
}
stopwatch.Stop();
Console.WriteLine($"\nTotal bytes returned: {total:#,#}");
Console.WriteLine($"Elapsed time: {stopwatch.Elapsed}\n");
}
static async Task<int> ProcessUrlAsync(string url, HttpClient client, CancellationToken token)
{
HttpResponseMessage response = await client.GetAsync(url, token);
byte[] content = await response.Content.ReadAsByteArrayAsync(token);
Console.WriteLine($"{url,-60} {content.Length,10:#,#}");
return content.Length;
}
}