CodeGym /Kurse /C# SELF /Abbrechen von Operationen: CancellationToken

Abbrechen von Operationen: CancellationToken

C# SELF
Level 60, Lektion 4
Verfügbar

1. Einführung in CancellationToken

Stell dir folgende Situation vor: du startest einen lang laufenden Dateidownload und erinnerst dich plötzlich, dass dein Internet nach Tarif abgerechnet wird und Gigabytes Goldwert sind. Oder der Nutzer hat eine Berechnung gestartet und merkt dann, dass er sie nicht mehr braucht. Er möchte auf „Abbrechen“ drücken und nicht warten. Moderne Anwendungen müssen responsiv sein — dafür müssen wir jederzeit ein Stoppsignal weitergeben können und asynchrone Operationen korrekt abbrechen.

Genau dafür gibt es in .NET die Abbruch-Infrastruktur — das CancellationToken.

CancellationToken (wörtlich „Abbruch-Token“) ist ein spezielles Objekt, das du in deine lang laufende Operation übergibst. Du kannst jederzeit über CancellationTokenSource ein Abbruchsignal senden, und die Operation sollte das Token periodisch prüfen (z. B. IsCancellationRequested) und sich korrekt beenden, bei Bedarf ThrowIfCancellationRequested() aufrufen.

Wie ist das aufgebaut? (Kurz)

  • Es gibt ein Objekt CancellationTokenSource, das Tokens „erzeugt“ und sie abbrechen kann (Methode Cancel()).
  • Das CancellationToken selbst ist eine Art „Signalflagge“, die an viele Operationen verteilt werden kann (über die Eigenschaft Token der Source).
  • Die Operation prüft periodisch das Token: wenn ein Abbruch angefordert wurde, beendet sie die Arbeit (oder wirft OperationCanceledException).

Analogie: du bist der Chef (du bist die CancellationTokenSource). Du gibst deinen Mitarbeitern „Badges“ (das sind die CancellationToken). Wenn du entscheidest, alles abzubrechen, hebst du die rote Flagge — und alle mit Badge rennen sofort weg und lassen ihr halbes Mittagessen zurück.

2. Wie man CancellationToken benutzt

Erstelle eine CancellationTokenSource (CancellationTokenSource)

var cts = new CancellationTokenSource();

Das Token bekommen (CancellationToken)

CancellationToken token = cts.Token;

Das Token an eine asynchrone Methode übergeben

Die meisten Standard-async-Methoden in .NET akzeptieren einen Parameter vom Typ CancellationToken. Zum Beispiel HttpClient.GetAsync, Stream.ReadAsync, Task.Delay und andere.

Beispiel — Delay mit Abbruch:

await Task.Delay(10000, token); // 10 Sekunden warten — aber abbrechbar!

Abbruch anfordern (z. B. per Taste oder Timer)

cts.Cancel(); // Alle Operationen mit diesem Token erfahren vom Abbruch 

Token innerhalb der Methode prüfen

Innerhalb deiner Methoden (besonders wenn die Arbeit lang und zyklisch ist) solltest du regelmäßig das Abbruch-Flag prüfen und eine OperationCanceledException werfen, falls Abbruch angefordert wurde:

token.ThrowIfCancellationRequested();

Oder einfach die Eigenschaft prüfen:

if (token.IsCancellationRequested)
{
    // Ressourcen freigeben, Methode verlassen
}

3. Beispiel: Abbruch in unsere Übungs-App einbauen

Angenommen, wir haben eine App, die Daten von einer Website runterlädt. Fügen wir die Möglichkeit hinzu, den Download abzubrechen, falls der Nutzer es sich anders überlegt.

Basisbeispiel für asynchronen Download

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

class Downloader
{
    public async Task DownloadAsync(string url)
    {
        var client = new HttpClient();
        string content = await client.GetStringAsync(url); // Ohne Abbruch
        Console.WriteLine("Download abgeschlossen!");
    }
}

CancellationToken hinzufügen

public async Task DownloadAsync(string url, CancellationToken token)
{
    var client = new HttpClient();
    string content = await client.GetStringAsync(url, token); // Jetzt mit Abbruch-Unterstützung!
    Console.WriteLine("Download abgeschlossen!");
}

Abbruch vom Aufrufer steuern

static async Task Main(string[] args)
{
    var downloader = new Downloader();
    var cts = new CancellationTokenSource();

    Console.WriteLine("Gib die URL zum Herunterladen ein:");
    string url = Console.ReadLine();

    var downloadTask = downloader.DownloadAsync(url, cts.Token);

    Console.WriteLine("Drücke eine beliebige Taste, um den Download abzubrechen...");
    Console.ReadKey();

    cts.Cancel(); // Abbruchsignal

    try
    {
        await downloadTask;
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("Download vom Benutzer abgebrochen!");
    }
}

So einfach! Jetzt kann der Benutzer die Operation jederzeit abbrechen.

4. Zusammenspiel von CancellationTokenSource und Methoden

flowchart TD
    A["Benutzer (Main)"] -- erstellt --> B["CancellationTokenSource"]
    B -- gibt aus --> C["CancellationToken"]
    C -- wird übergeben an --> D["Asynchrone Operation"]
    A -- ruft Cancel() auf --> B
    D -- prüft periodisch --> C
    C -- meldet Abbruch an --> D
    D -- wirft Exception oder beendet Arbeit --> A

5. Abbruchbehandlung

Wenn eine Operation ein Abbruch-Token erhält, gibt es zwei Verhaltensweisen:

.NET-Methoden werfen selbst die Exception.
Wenn du Standardmethoden wie Stream.ReadAsync, HttpClient.GetAsync oder Task.Delay aufrufst und ihnen das Token gibst, dann werfen diese Methoden selbst eine OperationCanceledException, sobald Cancel() gerufen wird. Du musst nur diese Exception abfangen.

Eigener asynchroner Code.
Wenn du eine eigene „lange“ Operation implementierst, z. B. eine zyklische Verarbeitung oder schwere Berechnungen, liegt es an dir, regelmäßig token.IsCancellationRequested zu prüfen (oder token.ThrowIfCancellationRequested() aufzurufen), um korrekt auf Abbruch zu reagieren.

Beispiel: Lange Operation mit manueller Abbruchprüfung

public async Task CalculatePrimesAsync(int max, CancellationToken token)
{
    for (int i = 2; i < max; i++)
    {
        token.ThrowIfCancellationRequested(); // Abbruch prüfen

        if (IsPrime(i))
        {
            Console.WriteLine($"Primzahl: {i}");
            await Task.Delay(100, token); // Kurze Pause (abbrechbar)
        }
    }
    Console.WriteLine("Berechnung abgeschlossen!");
}

private bool IsPrime(int n)
{
    for (int i = 2; i <= Math.Sqrt(n); i++)
        if (n % i == 0) return false;
    return true;
}

6. Nützliche Feinheiten

Methoden und Klassen, die CancellationToken unterstützen

Klasse/Methode Unterstützt CancellationToken? Beispiel
Task.Delay
✔
await Task.Delay(1000, token);
HttpClient.GetAsync
✔
await client.GetAsync(url, token);
Stream.ReadAsync
✔
await stream.ReadAsync(buffer, 0, len, token);
Task.Run
✔
Task.Run(() => ..., token);
File.ReadAllTextAsync
✔
await File.ReadAllTextAsync(path, token);
Thread.Sleep
✖ Unterstützt es nicht; besser Task.Delay verwenden
Deine Methoden ✔ (wenn du Unterstützung einbaust!)
MyLongMethod(token)

Lebenszyklus einer abbrechbaren Operation

sequenceDiagram
    participant Benutzer
    participant Main
    participant CancellationTokenSource
    participant AsynchroneOperation

    Benutzer->>Main: Startet Operation
    Main->>CancellationTokenSource: Erstellt CTS
    Main->>AsynchroneOperation: Startet und übergibt CancellationToken
    Benutzer->>Main: Drückt "Abbrechen"
    Main->>CancellationTokenSource: Ruft Cancel() auf
    AsynchroneOperation->>AsynchroneOperation: Bemerkt Abbruch (\nIsCancellationRequested)
    AsynchroneOperation-->>Main: Wirft OperationCanceledException
    Main->>Benutzer: Zeigt Nachricht "Operation abgebrochen"

Wo das in der Praxis benutzt wird

  • UI-Anwendungen: Lange Downloads, Berechnungen oder Dateioperationen abbrechen, wenn der Nutzer das Fenster schließt oder die Aktion storniert.
  • Server-Anwendungen: Wenn der Client die Verbindung trennt — besser die Anfrage abbrechen, um Ressourcen zu sparen.
  • Big-Data-Verarbeitung: Tasks können sehr lange laufen — immer Möglichkeit bereitstellen, Berechnungen oder Migrationen zu stoppen.
  • Integration mit Hardware: Scannen, Drucken und ähnliche Operationen müssen manchmal sofort abgebrochen werden — Abbruchunterstützung ist hier Pflicht.

7. Typische Fehler im Umgang mit CancellationToken

Fehler Nr.1: Das Token nicht prüfen.
Wenn die Operation token.IsCancellationRequested nicht prüft oder ThrowIfCancellationRequested() nicht aufruft, wird sie bei Abbruch nicht stoppen und Ressourcen verschwenden.

Fehler Nr.2: Falsche Behandlung von OperationCanceledException.
Wenn man OperationCanceledException nicht fängt, kann die Anwendung unerwartet enden. Verwende immer ein try-catch zur Abbruchbehandlung.

Fehler Nr.3: Ressourcenmanagement beim Abbruch nicht korrekt.
Ein Abbruch macht keine automatische Rückgängigmachung von Änderungen (z. B. Dateien oder Datenbank). Du musst Ressourcen im catch-Block manuell bereinigen.

Fehler Nr.4: Übergabe eines bereits abgebrochenen Tokens.
Wenn das Token schon abgebrochen ist, wirft die Methode sofort eine Exception, was die Logik stören kann, wenn das nicht berücksichtigt wird.

2
Aufgabe
C# SELF, Level 60, Lektion 4
Gesperrt
Suche nach Primzahlen mit Abbruchunterstützung
Suche nach Primzahlen mit Abbruchunterstützung
1
Umfrage/Quiz
Vergleich von Task und Thread, Level 60, Lektion 4
Nicht verfügbar
Vergleich von Task und Thread
Parallele Datenverarbeitung
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION