Kolumne: C# im Fokus

await-async asynchrone Programmierung in .NET 4.5
Kommentare

Das .NET Framework enthält verschiedene asynchrone Ausführungsmodelle, allerdings ist die Verwendung der vorhandenen Möglichkeiten nicht immer trivial. Zudem verlangen die vorhandenen Modelle teilweise mehrere Aufrufmethoden, die den entstehenden Quellcode verkomplizieren. Mit der Version 4.5 wird nun ein vereinfachtes Programmiermodell für die asynchrone Programmierung eingeführt. Mithilfe asynchroner Aufrufe gelingt es sehr einfach, langwierige Operationen in den Hintergrund zu verlegen.

Müssen zum Beispiel innerhalb einer Windows Presentation Foundation Daten über einen Web Service geladen werden, kann der Ladevorgang asynchron ausgeführt werden. Somit bleibt die Oberfläche während des Datenladens weiterhin für den Benutzer bedienbar, denn der asynchrone Datenabrufprozess wird vom UI Thread abgespalten und in einem eigenen Thread ausgeführt. Sobald die Daten abgerufen und verfügbar sind, werden sie dem ursprünglichen Thread übergeben. Die Nutzung von asynchronen Aufrufen befreit den Entwickler also von der direkten Verwendung und Verwaltung von Threads. Das .NET Framework besitzt bereits verschiedene Ansätze, um asynchrone Abläufe zu realisieren. Allerdings sind die bis zur Version 4.0 verfügbaren Pro¬grammiermodelle nicht sehr intuitiv. Um die asynchrone Programmierung in Zukunft zu erleichtern, führt das .NET Framework 4.5 daher zwei neue Schlüsselwörter ein.

Asynchrone Programmierung bis .NET 4.0

Das .NET Framework unterstützt bisher zwei wesentliche asynchrone Ansätze:

  • IAsyncResult Pattern (APM: Asynchronous Pattern Model)
  • Event-based Asynchronous Pattern (EAP)

Beide Ansätze unterscheiden sich in ihrer Umsetzung. Das APM besteht aus zwei Methoden: Eine Methode stellt den Startpunkt der Operation dar und trägt als Präfix die Bezeichnung Begin. Die Begin-Methode startet den asynchronen Prozess. Wenn er abgeschlossen ist, wird eine so genannte Rückruffunktion (Callback) aufgerufen. Dieser Methode wird als Präfix End vorangestellt. Beispiele für ein solches Methodenpaar sind zum Beispiel BeginDataRequest und EndDataRequest. Listing 1 zeigt dazu ein konkretes Beispiel. Wie dort erkennbar ist, stellt die Methode BeginRead der FileStream-Klasse den Startpunkt dar. Ihr wird neben einem Datenpuffer ein Funktionszeiger übergeben, der als Callback dient und aufgerufen wird, sobald der erste Datenpuffer eingelesen wurde. Im Beispiel wurde die Rückruffunktion als anonyme Methode realisiert. Weitere Informationen zu diesem Entwurfsmuster sind auf MSDN zu finden.

Event-based Asynchronous Pattern

Auch das Event-based Asynchronous Pattern besteht aus mehreren kooperierenden Methoden und Ereignissen. Die zugehörigen Methoden tragen das Suffix Async beziehungsweise Canceled. Zwingend vorhanden ist immer die XXXAsync-Methode, die in der Regel als Gegenstück zu einer synchronen Methode existiert. Die Methode XXXCanceled ist optional und nur vorhanden, wenn sie benötigt wird. Das Completed-Ereignis wird aufgerufen, wenn der asynchrone Aufruf ein Ergebnis zurückliefert. Optional kann auch ein ProgressChanged-Ereignis umgesetzt werden, das immer dann ausgelöst wird, wenn die im Hintergrund laufende Operation einen Fortschritt erzielt hat. Darüber ist dann zum Beispiel die Steuerung einer Prozentanzeige möglich. Mithilfe dieses Musters ist es sehr einfach, langwierige Operationen vom UI Thread abzukoppeln. Beim Aufruf einer XXXAsync-Methode wird intern ein Thread gestartet, der die geforderte Operation im Hintergrund ausführt. Sobald die Operation beendet wurde und ein zugehöriges XXXCompleted-Ereignis existiert, wird es im ursprünglichen Thread-Kontext ausgelöst. Innerhalb des Ereignisses kann so ohne Weiteres auf UI-Elemente zugegriffen werden. Listing 2 zeigt dazu ein Beispiel. Über eine Instanz der Klasse WebClient wird eine Ressource geladen. Damit der Ladevorgang nicht die Ausführung der Anwendung blockiert, wird der Vorgang asynchron ausgeführt. Nach Beendigung wird unabhängig davon, ob erfolgreich oder nicht, das Ereignis DownloadDataCompleted aufgerufen. Der Aufrufer kann dann den Status auswerten und entsprechend darauf reagieren.

Probleme bisheriger asynchroner Ansätze

Die bisher vorhandenen Entwurfsmuster wie APM und EAP ermöglichen nach einer gewissen Einarbeitungszeit die Auslagerung langwieriger Operationen in den Hintergrund. Trotzdem sind die Programmiermodelle immer noch komplex und benötigen mehrere Methoden beziehungsweise im Fall von EAP mehrere Ereignisse. Das führt in vielen Fällen dazu, dass der resultierende Quellcode unübersichtlich, schwer verständlich und unnötig komplex wird. Ebenfalls erschwert wird eine spätere Wartung beziehungsweise Erweiterung des Codes. Gerade die Implementierung mehrerer verteilter (Ereignis-)Methoden führt zu einem fragmentierten Quellcode, aus dem nicht immer sofort alle Zusammenhänge erkennbar werden. Abhilfe soll hier der neue Task-basierte asynchrone Ansatz (TAP) bringen, der nachfolgend erläutert wird.

Task-basierter asynchroner Ansatz

Um zukünftig die Implementierung asynchroner Lösungen zu vereinfachen, werden in der .NET-Version 4.5 zwei neue Schlüsselwörter eingeführt und die Sprachsyntax erweitert. Dabei handelt es sich zum einen um das Schlüsselwort async und zum anderen um das Schlüsselwort await. In Kooperation mit der Task-Klasse ermöglichen sie die vereinfachte Umsetzung asynchroner Aufrufe. Die explizite Definition von Rückruffunktionen (Callbacks) entfällt und der resultierende Quellcode bleibt strukturierter. Das zugehörige Entwurfsmuster trägt den passenden Namen Task-based Asynchronous Pattern (TAP). Listing 3 zeigt ein einfaches Verwendungsbeispiel. Wie daraus hervorgeht, ist keine Rückruffunktion oder zusätzliche Methode für den asynchronen Ablauf mehr notwendig. Die Methode DownloadData wurde in der Signatur mit dem neuen Schlüsselwort async versehen. Es signalisiert dem Compiler, dass die Methode asynchrone Aufrufe beinhaltet, bewirkt aber durch alleiniges Vorhandensein noch keine asynchrone Ausführung. Dazu müssen die Aufrufe erst noch mit dem Schlüsselwort await markiert werden. Eine Methode, die als async markiert wurde, aber keinen einzigen await-Aufruf enthält, verursacht einen Fehler während der Übersetzungszeit (Compile Time Check). Konkret wird der Teil der Methode asynchron ausgeführt, der dem await-Schlüsselwort folgt. Für das Beispiel aus Listing 3 bedeutet das, dass nun der Aufruf new WebClient(…) asynchron gestartet wird. Technisch gesehen wird an dieser Stelle eine Rückruffunktion etabliert, die nach Beendigung den Code an genau dieser Stelle fortsetzt. Ein weiteres Merkmal des Entwurfsmusters ist die Verwendung von Tasks. Die Methode DownloadData gibt ebenfalls eine Task-Instanz zurück. Generell können zwei Arten von Tasks zurückgegeben werden: Zum einen die mit einem Rückgabewert (Task>), zum anderen die ohne (nur Task). Liefert der asynchrone Aufruf kein Ergebnis wird Task oder der Typ Taskverwendet.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -