CodeGym /Kurse /C# SELF /Struktur einer asynchronen Methode (

Struktur einer asynchronen Methode ( async/ await)

C# SELF
Level 59, Lektion 3
Verfügbar

1. Einleitung

Stell dir vor, dein Code ist wie eine Warteschlange im Laden. Wenn du eine synchrone Methode aufrufst, wartet die ganze Schlange, bis der Kassierer einem Kunden fertig geholfen hat, bevor er den nächsten bedient. Ist die Methode asynchron, legst du deine Einkäufe an die Kasse und gehst deinen Dingen nach; wenn der Kassierer fertig ist, ruft er dich.

Eine asynchrone Methode (eine async-Methode) in .NET ist eine normale Methode mit einer besonderen "Beschriftung" davor (async), die es erlaubt, innerhalb den Operator await für nicht-blockierendes Warten auf andere asynchrone Operationen (z. B. Netzwerk- oder Dateioperationen) zu verwenden, ohne den Haupt-Thread zu blockieren. Genau diese "Beschriftung" verwandelt den langweiligen synchronen Laden in einen modernen, warteschlangenfreien Service.

Grundlegende Signatur einer asynchronen Methode

Eine asynchrone Methode wird unbedingt mit dem Modifikator async in der Signatur deklariert. Hier ein einfachstes Beispiel:

public async Task MyAsyncMethod()
{
    // Dein asynchroner Code hier
}

Rückgabetypen asynchroner Methoden:

Rückgabetyp Beschreibung Beispiel
Task
Die Methode führt Arbeit asynchron aus, gibt aber kein Ergebnis zurück
public async Task SaveFileAsync()
Task<T>
Die Methode führt Arbeit asynchron aus und liefert ein Ergebnis vom Typ T
public async Task<int> GetSumAsync()
void
Wird nur für Events verwendet. Im übrigen Code nicht empfohlen!
public async void Button_Click(...)
ValueTask / ValueTask<T>
Für hochperformante Szenarien, wenn das Ergebnis oft synchron bereitsteht —

Wichtig: Verwende async void nirgends außer in Event-Handlern! Sonst riskierst du, dass Fehler nicht richtig behandelt werden.

2. Einfaches Beispiel einer asynchronen Methode

Kehren wir zu unserer Übungsanwendung zurück (nehmen wir an, es ist ein normales Konsolenprogramm) und fügen eine asynchrone Methode hinzu, die Daten aus dem Netzwerk holt. Wir werden eine Verzögerung emulieren.

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("Starten der asynchronen Methode...");
        await MyFakeDownloadAsync();
        Console.WriteLine("Die asynchrone Operation ist abgeschlossen!");
    }

    static async Task MyFakeDownloadAsync()
    {
        Console.WriteLine("Beginne Download (emuliere 2 Sek Verzögerung)...");
        await Task.Delay(2000); // Emuliere eine lange Operation
        Console.WriteLine("Download abgeschlossen!");
    }
}

Kommentare zum Beispiel:

  • Das Schlüsselwort async steht bei Main und MyFakeDownloadAsync.
  • Der Operator await darf nur innerhalb von Methoden verwendet werden, die als async markiert sind.
  • Die Methode Task.Delay(2000) emuliert eine lange Operation (z. B. einen Netzwerkrequest).
  • Nach dem Auftauchen des Operators await wird der Thread nicht blockiert — die Ausführung von Main "pausiert" bis die Verzögerung beendet ist.

3. Wie funktioniert eine asynchrone Methode unter der Haube?

Wenn der Compiler eine Methode mit dem Schlüsselwort async sieht, verwandelt er sie in eine "State Machine", die die Stelle des Stopps, lokale Variablen speichert und die Ausführung nach Abschluss der awaited-Operation fortsetzt.

Hier eine stark vereinfachte Darstellung:


+-----------------------------------------------------------+
|          Aufruf der asynchronen Methode (z. B. await X)   |
+-------------------------+---------------------------------+
                          |
                          v
            Ist die Operation noch nicht abgeschlossen?
                |                  |
                | ja               | nein
                v                  v
          Unterbrechen           Sofort zurückgeben
          der Ausführung,        das Ergebnis
          Kontext merken         (oder eine Exception werfen)
                |
      Die asynchrone Operation beendet sich...
                |
                v
        Die State Machine "weckt" die Methode,
        die Ausführung geht nach dem await weiter

Es ist nicht nötig, die Implementierungsdetails auswendig zu kennen: der Kontext (Stoppstelle und lokale Variablen) wird gespeichert und automatisch wiederhergestellt, wenn die Operation abgeschlossen ist.

Wo man async und await verwenden kann (und wo nicht)

  • async wird vor der Methodendeklaration (oder Lambda) gesetzt.
  • Innerhalb einer async-Methode kann (und sollte) man await verwenden.
  • Wenn man versucht, await in einer Methode ohne async zu benutzen — gibt es einen Kompilierungsfehler.
  • Wenn async angegeben ist, aber im Methodenkörper kein await vorkommt — erhält man eine Warnung; die Methode läuft synchron und gibt eine bereits abgeschlossene Task zurück.

4. Varianten der Rückgabewerte und ihre Besonderheiten

Ergebnis Task

Wenn die Methode asynchrone Arbeit macht, aber keinen Wert zurückgibt, nutze Task:

public async Task SaveToFileAsync(string path)
{
    await Task.Delay(1000);
    // Speichere etwas in eine Datei
}

Ergebnis Task<T>

Wenn ein Ergebnis benötigt wird, verwende Task<T>:

public async Task<int> CalculateAsync()
{
    await Task.Delay(500);
    return 42;
}

Aufruf:

int result = await CalculateAsync();

Ergebnis async void

Nur für Events verwenden! Zum Beispiel ein Klick-Handler:

private async void Button_Click(object sender, EventArgs e)
{
    await Task.Delay(1000);
    // Mach noch etwas
}

Warum man async void in normalen Methoden vermeiden sollte? Weil du dann nicht erfährst, wann die Methode fertig ist, und Ausnahmen innerhalb nicht korrekt abfangen kannst.

Ergebnisse ValueTask und ValueTask<T>

Sie wurden für hochperformante Bibliotheken eingeführt, wenn das Ergebnis oft synchron fertig ist — sie vermeiden unnötige Task-Allocations. Zu Beginn deiner Karriere kannst du sie weglassen: sie kommen nicht so häufig vor.

5. Beispiel: Asynchroner Rechner mit Rückgabewert

Angenommen, das Programm berechnet die Summe von zwei Zahlen "mit Verzögerung", als wäre das eine sehr aufwändige Operation:

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("Gib die erste Zahl ein:");
        int a = int.Parse(Console.ReadLine()!);

        Console.WriteLine("Gib die zweite Zahl ein:");
        int b = int.Parse(Console.ReadLine()!);

        Console.WriteLine("Berechnung läuft...");
        int sum = await CalculateSumAsync(a, b);
        Console.WriteLine($"Summe: {sum}");
    }

    static async Task<int> CalculateSumAsync(int x, int y)
    {
        await Task.Delay(2000); // "Lange" Operation
        return x + y;
    }
}

Wichtig hier:

  • CalculateSumAsync ist eine asynchrone Methode, gibt Task<int> zurück und enthält ein await.
  • Der Aufruf in Main erfolgt mit await (Erinnerung: ab C# 7.1 darf Main asynchron sein).
  • Sobald die Summe berechnet wird, ist der Thread nicht blockiert — man kann Fortschritt anzeigen oder andere Arbeit erledigen.

6. Verschachtelte Methoden und verschachtelte await

Eine asynchrone Methode kann eine andere asynchrone Methode aufrufen, und diese wiederum noch eine. Die ganze Kette setzt sich automatisch fort:

public async Task<int> StartWorkAsync()
{
    int data = await GetDataAsync();
    int result = await ProcessDataAsync(data);
    return result;
}

Ganz einfach: erst warten wir auf den Abschluss einer Operation, dann auf die nächste.

7. Fallstricke und häufige Fehler

1. Vergiss async nicht!
Wenn du async vergisst, motzt der Compiler, wenn im Methodenkörper ein await steht, und du bekommst keinen lauffähigen Code.

2. Verwende async void nicht in normalen Methoden
Asynchrone void-Methoden (außer Event-Handler) sind schwarze Löcher für Exceptions. Fehler darin können "versinken" und du merkst es nicht.

3. Asynchrone Methode OHNE await
Das ist möglich, aber sinnlos — die Methode läuft synchron und gibt eine bereits abgeschlossene Task zurück.

public async Task DoNothingAsync()
{
    // Ups, kein await!
}

4. Rückgabe von Task aus einer NICHT-asynchronen Methode
Das macht man zur Vereinheitlichung von Schnittstellen: ein Teil der Operationen ist wirklich asynchron, andere sind sofort fertig.

public Task<int> Foo()
{
    // return 42; // So geht das nicht!
    return Task.FromResult(42); // Geht, aber hier gibt es keine Asynchronität.
}
2
Aufgabe
C# SELF, Level 59, Lektion 3
Gesperrt
Asynchrone Datenladung
Asynchrone Datenladung
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION