Asynchrone Programmierung in .NET 4.5 mit async und await

Seite 3: Klassenbibliothek, Debugging

Inhaltsverzeichnis

Nicht nur die Datenzugriffsklassen des Beispiels, sondern auch andere Klassen in der .NET-Klassenbibliothek haben asynchrone Methoden mit dem .NET Framework 4.5 erhalten; zum Beispiel haben Klassen im Namensraum System.IO nun neue asynchrone Methoden als Pendant zu bestehenden synchronen bekommen. Alle diese Methoden liefern einen Task<Typ> zurück:

  • Klasse System.IO.Stream: ReadAsync(), WriteAsync(), FlushAsync(), CopyToAsync()
  • Klasse System.IO.TextReader (bzw. davon abgeleitete Klassen wie StreamReader): ReadAsync(), ReadBlockAsync(), ReadLineAsync(), ReadToEndAsync()
  • Klasse System.IO.TextWriter (bzw. davon abgeleitete Klassen wie StreamWriter): WriteAsync(), WriteLineAsync(), FlushAsync()

Auch für die Netzwerkprogrammierung hat Microsoft neue asynchrone Methoden für die Task-basierte asynchrone Ausführung eingeführt. Bei den bestehenden Klassen wurde System.Net.WebClient um folgende Methoden erweitert:

  • OpenReadTaskAsync()
  • OpenWriteStringTaskAsync()
  • DownloadDataTaskAsync()
  • DownloadFileTaskAsync()
  • DownloadStringTaskAsync()
  • UploadDataTaskAsync()
  • UploadFileTaskAsync()
  • UploadStringTaskAsync()
  • UploadValuesTaskAsync()

Bei dem Namen fällt auf, dass sie auf "TaskAsync" enden, während in anderen FCL-Klassen der bisherige Name nur um "Async" erweitert wurde. Grund für die Abweichung ist, dass die Klasse WebClient bereits in .NET2.0 asynchrone Methoden (z. B. [i]DownloadStringAsync()) erhalten hat, die einem anderen Entwurfsmuster folgen: Sie lösen ein Ereignis aus (z. B. DownloadStringCompleted()). Dadurch war der Name mit "Async" am Ende schon belegt.

Neben den Erweiterungen für die Klasse System.Net.WebClient gibt es noch die neue Klasse System.Net.Http.HttpClient. Microsoft nennt fünf Argumente, warum sie besser als die alte sei. Allerdings sind diese nicht schlüssig, da man nicht nur von HttpClient, sondern auch von WebClient erben kann. Auch dieser kann viele Anfragen gleichzeitig verwalten, und ebenso unterstützt nun WebClient asynchrone Methoden mit dem neuen Task-Entwurfsmuster. Was dort nicht verraten wird, steht in Diskussionsbeiträgen: HttpClient arbeitet intern viel stärker asynchron als WebClient. Dennoch bleibt die Frage, warum Microsoft schon wieder eine neue API gestalten musste.

Auch die .NET-Klasse System.Net.Sockets.Socket hat jetzt asynchrone Methoden. Beim Erstellen von Proxies für WCF-Dienste (Windows Communication Foundation) bietet Visual Studio nun die Option, statt der bisherigen asynchronen Operationen mit Rückrufereignis auch Methoden gemäß TAP zu erzeugen. Aber Achtung: In beiden Fällen (s. Abb. 5) ist der Methodenname DoWorkAsync(). Bei TAP Pattern gibt es dann jedoch kein DoWorkCompleted()-Ereignis. Der Entwickler muss also aufpassen, dass er da nicht beim Neugenerieren von WCF-Proxies die falsche Option erwischt und dann der Programmcode nicht mehr läuft.

Bei WCF-Proxies hat man die Wahl zwischen dem Task-based Asynchrous Pattern aus NET 4.5 (roter Pfeil) und den alten Async-Event Pattern aus .NET 2.0 (Abb. 5).

Beim Design der Klassen in der mit Windows 8 eingeführten Windows Runtime Library (WinRT) hat Microsoft systematisch das Task-Based Asynchronous Pattern umgesetzt. "Alles, was über 50 Millisekunden dauern könnte, hat nun nur noch asynchrone Methoden", sagte Aleš Holeček auf der BUILD-2011-Konferenz, auf der Microsoft WinRT erstmals vorstellte. Das Unternehmen will die Entwickler erziehen, die aus Vereinfachungsgründen bisher den weit leichteren, aber den Thread blockierenden synchronen Weg gewählt haben. Listing 2 zeigt eine Dateisuche mit WinRT auf Basis von Windows (Desktop) Search, implementiert in .NET 4.5 mit C# 5.0 in einer Konsolenanwendung und unter Verwendung der WinRT-Klassen im Namensraum Windows.Storage.Search und Windows.Storage.

Eine Unterstützung für async und await gibt es noch nicht im objektrelationalen Mapper Entity Framework 5.0, der mit Visual Studio 2012 ausgeliefert wird. Asynchrone Methoden auf Basis des Task-based Asynchronous Pattern wird man erst in Version 6.0 erleben, die derzeit als zweite Alpha-Version vorliegt. Dort gibt es dann asynchrone Methoden sowohl für die Datenabrufe (z. B. ToListAsync(), ToArrayAsync()SingleAsync(), FirstAsync(), SingleOrDefaultAsync() und FirstOrDefaultAsync, CountAsync(), AllAsync(), AnyAsync(), AverageAsync(), MinAsync(), MaxAsync() und SumAsyn()) als auch zum Speichern von neuen, geänderten und gelöschten Objekten (SaveChangesAsync()).

Das Debugging von Anwendungen mit paralleler Verarbeitung ist nicht trivial. Zusätzlich zu dem schon lange vorhandenen "Threads"-Fenster gibt es seit Visual Studio 2010 die "Parallel Tasks" und seit Visual Studio 2012 nun auch "Parallel Stacks" (s. Abb. 6). "Threads" zeigt alle parallel laufenden Threads – auch solche, die Visual Studio zum Debugging braucht. Die "Parallel Tasks"-Liste beschränkt sich auf die aus der Anwendung heraus gestarteten Tasks der TPL. Bei der neuen grafischen Ansicht der "Parallel Stacks" kann man zwischen einer Ansicht der Threads und der der Tasks umschalten. In ihr zeigt ein Kasten jeweils die Aufrufreihenfolge von Methoden. Pfeile repräsentieren Verzweigungen, bei denen die Threads eines Kastens verschiedene Folgemethoden zufällig ausgewählt haben.

In Abbildung 6 zeigt "Parallel Stacks" an, dass sich drei Threads in DoWorkInternal1() befinden. Hingegen sind zwei Threads über DoWorkInternal2() gelaufen, wobei einer der Threads nach DoWorkInternal3() weiterverzweigt ist. Ein weiterer Thread ist im Hauptprogramm (Main). Das korrespondiert mit den Anzeigen in "Threads" und "Parallel Stack". Der gelbe Pfeil in "Parallel Stacks" zeigt an, in welchem Thread sich der Debugger gerade bei der aktuellen Programmcodezeile befindet (hier: Zeile 105 in DoWorkInternal3()). Im "Parallel Stacks" ist der Weg des aktuellen Threads durch blaue Rahmen und Pfeile hervorgehoben.

Debugging-Fenster "Parallel Stacks", "Parallel Tasks" und "Threads" in Visual Studio 2012 (Abb. 6)