Asynchrone Methoden einfach erstellen: ASYNC und AWAIT

Lange Wartezeiten auf Rückmeldung des Computers bei komplexen Programmierroutinen gehören unter .Net wohl bald der Vergangenheit an. Das Framework ab Version 4.5 bietet recht einfache Mittel, um einen Programmaufruf „asynchron“ zu gestalten. Die Zauberworte dazu sind „ASYNC“ und „AWAIT“.


War es bis Framework 4.0 noch ein wenig umständlich, eine Routine so aufzurufen, dass die übrigen Befehle weiter abgearbeitet wurden, so kann man heute fast automatisiert einen bequemeren Weg gehen. Ich zeige Ihnen hier anhand eines Beispiels, wie eine Routine aufgerufen wird, die asynchron ablaufen soll.
Das Beispiel beinhaltet eine Form (WPF), und eine Routine, die Asynchron eine Liste von Webseiten besucht und deren Größen ermittelt. Basis für diese Solution ist das Walk-Through-Beispiel von Microsoft, daher auch die vielen Links zu Microsoft-Webseiten.
Das Beispiel wurde in eine eigene Solution eingestellt, mit einer Form umgeben und insofern erweitert, als das es hier nun möglich ist, eigene Texte zu erfassen während im Hintergrund die Webseiten durchforstet werden.

Hinweis: Der hier angegebene Code wurde gekürzt und ist so nicht ablauffähig. Am Ende des Artikels können Sie die lauffähige Solution herunterladen und damit experimentieren.

Eine asynchrone Methode unterscheidet sich in zwei Dingen von einer normalen Methode:
– Sie beginnt mit dem Schlüsselwort „ASYNC“
– Sie liefert als Function immer das geforderte Objekt eingebettet in einem Task mit (bei einer Prozedur wird immer nur der Task zurückgeliefert).

Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())

Der Aufruf einer asynchronen Funktion wird wie gehabt durchgeführt, allerdings kann der Fortgang dieser Routine mit dem Schlüsselwort „AWAIT“ solange angehalten werden, bis die asynchrone Aufgabe erledigt ist:.

Dim getContentsTask As Task(Of Byte()) = Nothing
Dim urlContents As Byte() = Nothing
getContentsTask = GetURLContentsAsync(url)
urlContents = Await getContentsTask
'Rest der Routine wird erst abgearbeitet, wenn getContentsTask fertig ist...

D.h. der aufrufende asynchrone Job selbst läuft nicht weiter, aber die Anwendung als solche kann neue Befehle entgegennehmen. In dem Beispiel kann Text in eine Textbox eingegeben werden und der Text wird zu dem bisherigen Ergebnis in einem Label angezeigt, und das alles, während der asynchrone Job noch läuft.


Imports System.IO
Imports System.Net

Class MainWindow

'''
<summary>E: Start Async-Task
''' D: Asynchrone Aufgabe starten</summary>
Async Sub OnAsyncClick(sender As Object, e As RoutedEventArgs) Handles m_AsyncButton.Click
' Two-step async call.
Dim sumTask As Task = SumPageSizesAsync()
'AWAIT-State: the following steps in this method will only be processed when sumTask has finished!
Await sumTask

'...
End Sub

'''
<summary>E: assign textbox-text to result-label in sync-mode
''' D: Synchrone Uebertragung des Textes in die Ergebnisbox</summary>
Private Sub m_SyncButton_Click(sender As Object, e As RoutedEventArgs) Handles m_SyncButton.Click
resultLabelAsync.Content &= String.Format(vbCrLf & "{0,-58}", myTextBox.Text)
myTextBox.Text = ""
End Sub

Private Async Function SumPageSizesAsync() As Task

' Make a list of web addresses.
Dim urlList As List(Of String) = SetUpURLList() 'Routine liefert eine Liste mit URLs

Dim total = 0
For Each url In urlList
' GetURLContentsAsync returns a task. At completion, the task
' produces a byte array.
Dim getContentsTask As Task(Of Byte()) = Nothing
Dim urlContents As Byte() = Nothing
Try
getContentsTask = GetURLContentsAsync(url)

urlContents = Await getContentsTask
Catch ex As Exception
'Never do this in productive-environments!
'D: NIE in Produktivumgebungen so machen!
ex = Nothing
End Try
Next
End Function

Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())
' The downloaded resource ends up in the variable named content.
Dim content = New MemoryStream()
' Initialize an HttpWebRequest for the current URL.
Dim webReq = CType(WebRequest.Create(url), HttpWebRequest)
' Send the request to the Internet resource and wait for
' the response.
Dim responseTask As Task(Of WebResponse) = webReq.GetResponseAsync()

Try

Using response As WebResponse = Await responseTask

' Get the data stream that is associated with the specified URL.
Using responseStream As Stream = response.GetResponseStream()
' Read the bytes in responseStream and copy them to content.
' CopyToAsync returns a Task, not a Task.
Dim copyTask As Task = responseStream.CopyToAsync(content)

' When copyTask is completed, content contains a copy of
' responseStream.
Await copyTask

End Using
End Using
Catch ex As Exception 'e.g. URL not found
'Never do this in productive-environments!
'D: NIE in Produktivumgebungen so machen!
ex = Nothing
End Try
' Return the result as a byte array.
Return content.ToArray()
End Function

End Class

Ich empfinde das als riesige Erleichterung gegenüber den in den Vorgängerversionen notwendigen Mechanismus, wenngleich damit kein Multitasking eingeleitet wird! Es wird nämlich weder ein eigener Thread geschweige denn ein eigener Task aufgebaut!

Das komplette Beispiel als VS2012-Solution können Sie hier herunterladen.

Hinweis: .NET Framework 4.5 steht erst ab Sommer/Herbst 2012 zur Verfügung. Hier kann eine Vorab-Version installiert werden: FrameworkVS 2012

Haben Sie auch schon erste Schritte mit dem Framework 4.5 gemacht? Schreiben Sie hier Ihre Ergebnisse rein, und wenn Sie Fragen haben dürfen Sie mich gerne kontaktieren.

Advertisements
%d Bloggern gefällt das: