Freigeben über


Ablauf des erneuten Eintretens in asynchronen Anwendungen (Visual Basic)

Wenn Sie asynchronen Code in der App einschließen, sollten Sie erneutes Eintreten, also den erneuten Beginn eines asynchronen Vorgangs vor seinem Abschließen, berücksichtigen und möglicherweise verhindern. Wenn Sie Möglichkeiten für erneutes Eintreten nicht identifizieren und behandeln, kann dies zu unerwarteten Ergebnissen führen.

Hinweis

Zum Ausführen des Beispiels müssen Sie Visual Studio 2012 oder höher und .NET Framework 4.5 oder höher auf Ihrem Computer installiert haben.

Hinweis

Transport Layer Security (TLS) Version 1.2 ist jetzt die Mindestversion für die App-Entwicklung. Wenn Ihre App auf eine .NET Framework-Version vor 4.7 ausgerichtet ist, lesen Sie den folgenden Artikel für bewährte Methoden für Transport Layer Security (TLS) mit .NET Framework.

Erkennen von Ablaufinvarianz

Im Beispiel in diesem Thema wählen Benutzer eine Schaltfläche " Start " aus, um eine asynchrone App zu initiieren, die eine Reihe von Websites herunterlädt, und berechnet die Gesamtanzahl der heruntergeladenen Bytes. Eine synchrone Version des Beispiels reagiert auf die gleiche Weise, unabhängig davon, wie oft ein Benutzer die Schaltfläche auswäht, da der UI-Thread nach dem ersten Mal diese Ereignisse ignoriert, bis die App ausgeführt wird. In einer asynchronen App reagiert der UI-Thread jedoch weiterhin, und Sie können den asynchronen Vorgang erneut ausführen, bevor er abgeschlossen wurde.

Das folgende Beispiel zeigt die erwartete Ausgabe, wenn der Benutzer die Schaltfläche "Start " nur einmal auswäht. Eine Liste der heruntergeladenen Websites wird mit der Größe jeder Website in Byte angezeigt. Die Gesamtanzahl der Bytes wird am Ende angezeigt.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

Wenn der Benutzer die Schaltfläche jedoch mehrmals auswähelt, wird der Ereignishandler wiederholt aufgerufen, und der Downloadvorgang wird jedes Mal erneut aufgerufen. Daher werden mehrere asynchrone Vorgänge gleichzeitig ausgeführt, die Ausgabe interleasiert die Ergebnisse, und die Gesamtanzahl der Bytes ist verwirrend.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
6. msdn.microsoft.com/library/ms404677.aspx               197325
3. msdn.microsoft.com/library/jj155761.aspx                29019
7. msdn.microsoft.com                                            42972
4. msdn.microsoft.com/library/hh290140.aspx               117152
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

5. msdn.microsoft.com/library/hh524395.aspx                68959
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
6. msdn.microsoft.com/library/ms404677.aspx               197325
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
7. msdn.microsoft.com                                            42972
5. msdn.microsoft.com/library/hh524395.aspx                68959
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

Sie können den Code überprüfen, der diese Ausgabe erzeugt, indem Sie zum Ende dieses Themas scrollen. Sie können mit dem Code experimentieren, indem Sie die Lösung auf Ihren lokalen Computer herunterladen und dann das WebsiteDownload-Projekt ausführen oder den Code am Ende dieses Themas verwenden, um Ihr eigenes Projekt zu erstellen. Weitere Informationen und Anweisungen finden Sie unter "Überprüfen und Ausführen der Beispiel-App".

Umgang mit Ablaufinvarianz

Sie können den Wiedereintritt auf unterschiedliche Weise behandeln, abhängig davon, was Ihre App tun soll. In diesem Thema werden die folgenden Beispiele vorgestellt:

Schaltfläche "Start" deaktivieren

Sie können die Startschaltfläche blockieren, während ein Vorgang ausgeführt wird, indem Sie die Schaltfläche am oberen Rand des StartButton_Click Ereignishandlers deaktivieren. Sie können die Schaltfläche dann von innerhalb eines Finally Blocks wieder aktivieren, wenn der Vorgang abgeschlossen ist, damit Benutzer die App erneut ausführen können.

Der folgende Code zeigt diese Änderungen, die mit Sternchen gekennzeichnet sind. Sie können die Änderungen am Ende dieses Themas dem Code hinzufügen oder die fertige App aus Async Samples herunterladen: Reentrancy in .NET Desktop Apps. Der Projektname ist DisableStartButton.

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' This line is commented out to make the results clearer in the output.
    'ResultsTextBox.Text = ""

    ' ***Disable the Start button until the downloads are complete.
    StartButton.IsEnabled = False

    Try
        Await AccessTheWebAsync()

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    ' ***Enable the Start button in case you want to run the program again.
    Finally
        StartButton.IsEnabled = True

    End Try
End Sub

Aufgrund der Änderungen reagiert die Schaltfläche nicht, während AccessTheWebAsync die Websites heruntergeladen werden, sodass der Vorgang nicht erneut ausgeführt werden kann.

Abbrechen und Neustarten des Vorgangs

Anstatt die Schaltfläche "Start " zu deaktivieren, können Sie die Schaltfläche aktiv halten, aber wenn der Benutzer diese Schaltfläche erneut auswählt, brechen Sie den bereits ausgeführten Vorgang ab, und lassen Sie den zuletzt gestarteten Vorgang fortsetzen.

Weitere Informationen zum Abbruch finden Sie unterFine-Tuning Ihre Asynchrone Anwendung (Visual Basic).For more information about cancellation, seeFine-Tuning Your Async Application (Visual Basic).

Um dieses Szenario einzurichten, nehmen Sie die folgenden Änderungen am grundlegenden Code vor, der in der Überprüfung und Ausführung der Beispiel-App bereitgestellt wird. Sie können die fertige App auch aus Async Samples herunterladen: Reentrancy in .NET Desktop Apps. Der Name dieses Projekts lautet CancelAndRestart.

  1. Deklarieren Sie eine CancellationTokenSource Variable, ctsdie sich für alle Methoden im Gültigkeitsbereich befindet.

    Class MainWindow // Or Class MainPage
    
        ' *** Declare a System.Threading.CancellationTokenSource.
        Dim cts As CancellationTokenSource
    
  2. Ermitteln Sie in StartButton_Click, ob bereits ein Vorgang ausgeführt wird. Wenn der Wert cts ist Nothing, ist kein Vorgang bereits aktiv. Wenn der Wert nicht Nothingist, wird der bereits ausgeführte Vorgang abgebrochen.

    ' *** If a download process is already underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If
    
  3. Legen Sie diesen cts Wert auf einen anderen Wert fest, der den aktuellen Prozess darstellt.

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS
    
  4. Am Ende des StartButton_Click ist der aktuelle Prozess abgeschlossen. Setzen Sie daher den Wert von cts zurück auf Nothing.

    ' *** When the process completes, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
    

Der folgende Code zeigt alle Änderungen in StartButton_Click. Die Ergänzungen sind mit Sternchen gekennzeichnet.

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)

    ' This line is commented out to make the results clearer.
    'ResultsTextBox.Text = ""

    ' *** If a download process is underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS

    Try
        ' *** Send a token to carry the message if the operation is canceled.
        Await AccessTheWebAsync(cts.Token)

    Catch ex As OperationCanceledException
        ResultsTextBox.Text &= vbCrLf & "Download canceled." & vbCrLf

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    End Try

    ' *** When the process is complete, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
End Sub

Nehmen Sie in AccessTheWebAsync die folgenden Änderungen vor.

  • Fügen Sie einen Parameter hinzu, um das Storniertoken von StartButton_Click zu akzeptieren.

  • Verwenden Sie die GetAsync Methode, um die Websites herunterzuladen, weil GetAsync ein CancellationToken Argument akzeptiert.

  • Bevor Sie DisplayResults aufrufen, um die Ergebnisse für jede heruntergeladene Website anzuzeigen, aktivieren Sie ct, um sicherzustellen, dass der aktuelle Vorgang nicht abgebrochen wurde.

Der folgende Code zeigt diese Änderungen, die mit Sternchen gekennzeichnet sind.

' *** Provide a parameter for the CancellationToken from StartButton_Click.
Private Async Function AccessTheWebAsync(ct As CancellationToken) As Task

    ' Declare an HttpClient object.
    Dim client = New HttpClient()

    ' Make a list of web addresses.
    Dim urlList As List(Of String) = SetUpURLList()

    Dim total = 0
    Dim position = 0

    For Each url In urlList
        ' *** Use the HttpClient.GetAsync method because it accepts a
        ' cancellation token.
        Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

        ' *** Retrieve the website contents from the HttpResponseMessage.
        Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()

        ' *** Check for cancellations before displaying information about the
        ' latest site.
        ct.ThrowIfCancellationRequested()

        position += 1
        DisplayResults(url, urlContents, position)

        ' Update the total.
        total += urlContents.Length
    Next

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function

Wenn Sie während der Ausführung dieser App mehrmals die Schaltfläche "Start " auswählen, sollte sie Ergebnisse erzeugen, die der folgenden Ausgabe ähneln:

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               122505
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
Download canceled.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
Download canceled.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

Zum Ausschließen der Teillisten entfernen Sie die Kommentarmarkierungen der ersten Codezeile in StartButton_Click, um das Textfeld bei jedem erneuten Start des Vorgangs zu löschen.

Ausführen mehrerer Vorgänge und Einreihen der Ausgabe in die Warteschlange

Dieses dritte Beispiel ist das komplexeste, da die App jedes Mal, wenn der Benutzer die Startschaltfläche auswählt, einen weiteren asynchronen Vorgang startet und alle Vorgänge bis zum Abschluss ausgeführt werden. Alle angeforderten Vorgänge laden Websites asynchron aus der Liste herunter, die Ausgabe der Vorgänge wird jedoch sequenziell dargestellt. Das bedeutet, die tatsächliche Downloadaktivität überlappt, wie es die Ausgabe in Erkennen von Ablaufinvarianz zeigt, die Ergebnislisten für jede Gruppe aber getrennt angezeigt werden.

Die Vorgänge geben global Task, pendingWork frei, der als Gatekeeper für den Anzeigenprozess dient.

Sie können dieses Beispiel ausführen, indem Sie die Änderungen in den Code in das Erstellen der App einfügen, oder Sie können den Anweisungen zum Herunterladen der App folgen, um das Beispiel herunterzuladen und dann das QueueResults-Projekt auszuführen.

Die folgende Ausgabe zeigt das Ergebnis an, wenn der Benutzer die Schaltfläche "Start " nur einmal auswäht. Das Buchstabenetikett A gibt an, dass das Ergebnis beim ersten Auswählen der Schaltfläche "Start " stammt. Die Zahlen zeigen die Reihenfolge der URLs in der Liste der Downloadziele an.

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/library/aa578028.aspx               209858
A-3. msdn.microsoft.com/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/library/hh524395.aspx                71260
A-6. msdn.microsoft.com/library/ms404677.aspx               199186
A-7. msdn.microsoft.com                                            53266
A-8. msdn.microsoft.com/library/ff730837.aspx               148020

TOTAL bytes returned:  918876

#Group A is complete.

Wenn der Benutzer dreimal die Startschaltfläche auswäht, erzeugt die App Ausgaben, die den folgenden Zeilen ähneln. Die Informationszeilen, die mit einem Rautezeichen (#) beginnen, verfolgen den Fortschritt der Anwendung.

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/library/aa578028.aspx               207089
A-3. msdn.microsoft.com/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/library/hh524395.aspx                71259
A-6. msdn.microsoft.com/library/ms404677.aspx               199185

#Starting group B.
#Task assigned for group B.

A-7. msdn.microsoft.com                                            53266

#Starting group C.
#Task assigned for group C.

A-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  916095

B-1. msdn.microsoft.com/library/hh191443.aspx                87389
B-2. msdn.microsoft.com/library/aa578028.aspx               207089
B-3. msdn.microsoft.com/library/jj155761.aspx                30870
B-4. msdn.microsoft.com/library/hh290140.aspx               119027
B-5. msdn.microsoft.com/library/hh524395.aspx                71260
B-6. msdn.microsoft.com/library/ms404677.aspx               199186

#Group A is complete.

B-7. msdn.microsoft.com                                            53266
B-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  916097

C-1. msdn.microsoft.com/library/hh191443.aspx                87389
C-2. msdn.microsoft.com/library/aa578028.aspx               207089

#Group B is complete.

C-3. msdn.microsoft.com/library/jj155761.aspx                30870
C-4. msdn.microsoft.com/library/hh290140.aspx               119027
C-5. msdn.microsoft.com/library/hh524395.aspx                72765
C-6. msdn.microsoft.com/library/ms404677.aspx               199186
C-7. msdn.microsoft.com                                            56190
C-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  920526

#Group C is complete.

Gruppen B und C beginnen, bevor Gruppe A abgeschlossen ist, die Ausgabe für jede Gruppe wird jedoch separat angezeigt. Alle Ausgaben für Gruppe A werden zuerst angezeigt, gefolgt von der gesamten Ausgabe für Gruppe B und dann alle Ausgaben für Gruppe C. Die App zeigt die Gruppen immer in der reihenfolge an und zeigt für jede Gruppe immer die Informationen zu den einzelnen Websites in der Reihenfolge an, in der die URLs in der Liste der URLs angezeigt werden.

Sie können jedoch nicht vorhersagen, in welcher Reihenfolge die Downloads tatsächlich auftreten. Nachdem mehrere Gruppen gestartet wurden, sind die von ihnen generierten Downloadaufgaben alle aktiv. Sie können nicht davon ausgehen, dass A-1 vor B-1 heruntergeladen wird, und Sie können nicht davon ausgehen, dass A-1 vor A-2 heruntergeladen wird.

Globale Definitionen

Der Beispielcode enthält die folgenden beiden globalen Deklarationen, die von allen Methoden sichtbar sind.

Class MainWindow    ' Class MainPage in Windows Store app.

    ' ***Declare the following variables where all methods can access them.
    Private pendingWork As Task = Nothing
    Private group As Char = ChrW(AscW("A") - 1)

Die Task Variable pendingWorküberwacht den Anzeigevorgang und verhindert, dass eine Gruppe den Anzeigevorgang einer anderen Gruppe unterbricht. Die Zeichenvariable group, bezeichnet die Ausgabe aus verschiedenen Gruppen, um zu überprüfen, ob die Ergebnisse in der erwarteten Reihenfolge angezeigt werden.

Der Click-Ereignishandler

Mit dem Ereignishandler StartButton_Click wird der Gruppenbuchstabe bei jedem Auswählen der Schaltfläche Start erhöht. Dann ruft der Handler AccessTheWebAsync auf, um den Downloadvorgang auszuführen.

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' ***Verify that each group's results are displayed together, and that
    ' the groups display in order, by marking each group with a letter.
    group = ChrW(AscW(group) + 1)
    ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Starting group {0}.", group)

    Try
        ' *** Pass the group value to AccessTheWebAsync.
        Dim finishedGroup As Char = Await AccessTheWebAsync(group)

        ' The following line verifies a successful return from the download and
        ' display procedures.
        ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Group {0} is complete." & vbCrLf, finishedGroup)

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."

    End Try
End Sub

Die AccessTheWebAsync-Methode

In diesem Beispiel wird AccessTheWebAsync in zwei Methoden aufgeteilt. Die erste Methode AccessTheWebAsync startet alle Downloadaufgaben für eine Gruppe und richtet pendingWork ein, um den Anzeigevorgang zu steuern. Die Methode verwendet eine LINQ-Abfrage (Language Integrated Query) und ToArray startet alle Downloadaufgaben gleichzeitig.

AccessTheWebAsync ruft dann FinishOneGroupAsync auf, um den Abschluss jedes Downloads abzuwarten und dessen Länge anzuzeigen.

FinishOneGroupAsync gibt eine Aufgabe zurück, die pendingWork in AccessTheWebAsync zugewiesen ist. Dieser Wert verhindert Unterbrechungen durch einen anderen Vorgang, bevor die Aufgabe abgeschlossen ist.

Private Async Function AccessTheWebAsync(grp As Char) As Task(Of Char)

    Dim client = New HttpClient()

    ' Make a list of the web addresses to download.
    Dim urlList As List(Of String) = SetUpURLList()

    ' ***Kick off the downloads. The application of ToArray activates all the download tasks.
    Dim getContentTasks As Task(Of Byte())() =
        urlList.Select(Function(addr) client.GetByteArrayAsync(addr)).ToArray()

    ' ***Call the method that awaits the downloads and displays the results.
    ' Assign the Task that FinishOneGroupAsync returns to the gatekeeper task, pendingWork.
    pendingWork = FinishOneGroupAsync(urlList, getContentTasks, grp)

    ResultsTextBox.Text &=
        String.Format(vbCrLf & "#Task assigned for group {0}. Download tasks are active." & vbCrLf, grp)

    ' ***This task is complete when a group has finished downloading and displaying.
    Await pendingWork

    ' You can do other work here or just return.
    Return grp
End Function

Die FinishOneGroupAsync-Methode

Diese Methode durchgibt die Downloadaufgaben in einer Gruppe, wartet auf jeden einzelnen, zeigt die Länge der heruntergeladenen Website an und fügt die Länge zum Gesamtwert hinzu.

Die erste Anweisung in FinishOneGroupAsync verwendet pendingWork, um sicherzustellen, dass das Betreten der Methode nicht in einen Vorgang eingreift, der sich bereits im Anzeigeprozess befindet oder bereits wartet. Wenn ein solcher Vorgang ausgeführt wird, wird der eintretende Vorgang solange aufgeschoben, bis er an der Reihe ist.

Private Async Function FinishOneGroupAsync(urls As List(Of String), contentTasks As Task(Of Byte())(), grp As Char) As Task

    ' Wait for the previous group to finish displaying results.
    If pendingWork IsNot Nothing Then
        Await pendingWork
    End If

    Dim total = 0

    ' contentTasks is the array of Tasks that was created in AccessTheWebAsync.
    For i As Integer = 0 To contentTasks.Length - 1
        ' Await the download of a particular URL, and then display the URL and
        ' its length.
        Dim content As Byte() = Await contentTasks(i)
        DisplayResults(urls(i), content, i, grp)
        total += content.Length
    Next

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function

Sie können dieses Beispiel ausführen, indem Sie die Änderungen in den Code in das Erstellen der App einfügen, oder Sie können den Anweisungen zum Herunterladen der App folgen, um das Beispiel herunterzuladen, und dann das QueueResults-Projekt ausführen.

Interessante Punkte

Die Informationszeilen, die mit einem Nummernzeichen (#) in der Ausgabe beginnen, erläutern die Funktionsweise dieses Beispiels.

Die Ausgabe zeigt die folgenden Muster.

  • Eine Gruppe kann gestartet werden, während eine vorherige Gruppe ihre Ausgabe anzeigt, aber die Anzeige der Ausgabe der vorherigen Gruppe wird nicht unterbrochen.

    #Starting group A.
    #Task assigned for group A. Download tasks are active.
    
    A-1. msdn.microsoft.com/library/hh191443.aspx                87389
    A-2. msdn.microsoft.com/library/aa578028.aspx               207089
    A-3. msdn.microsoft.com/library/jj155761.aspx                30870
    A-4. msdn.microsoft.com/library/hh290140.aspx               119037
    A-5. msdn.microsoft.com/library/hh524395.aspx                71260
    
    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    
    A-6. msdn.microsoft.com/library/ms404677.aspx               199186
    A-7. msdn.microsoft.com                                            53078
    A-8. msdn.microsoft.com/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915919
    
    B-1. msdn.microsoft.com/library/hh191443.aspx                87388
    B-2. msdn.microsoft.com/library/aa578028.aspx               207089
    B-3. msdn.microsoft.com/library/jj155761.aspx                30870
    
    #Group A is complete.
    
    B-4. msdn.microsoft.com/library/hh290140.aspx               119027
    B-5. msdn.microsoft.com/library/hh524395.aspx                71260
    B-6. msdn.microsoft.com/library/ms404677.aspx               199186
    B-7. msdn.microsoft.com                                            53078
    B-8. msdn.microsoft.com/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915908
    
  • Der pendingWork-Task ist zu Beginn von Nothing nur für Gruppe A FinishOneGroupAsync, die zuerst gestartet wurde. Gruppe A hat bei Erreichen von FinishOneGroupAsync einen Erwartungsausdruck noch nicht abgeschlossen. Daher wurde die Kontrolle nicht an AccessTheWebAsync zurückgegeben, und die erste Zuordnung zu pendingWork ist noch nicht erfolgt.

  • Die folgenden beiden Zeilen werden immer in der Ausgabe zusammen angezeigt. Der Code wird nie unterbrochen zwischen dem Starten eines Vorgangs einer Gruppe in StartButton_Click und der Zuweisung einer Aufgabe an die Gruppe in pendingWork.

    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    

    Nachdem eine Gruppe in StartButton_Click eintritt, schließt der Vorgang einen Erwartungsausdruck erst ab, wenn der Vorgang in FinishOneGroupAsync eintritt. Daher kann kein anderer Vorgang während dieses Codesegments die Kontrolle erlangen.

Überprüfen und Ausführen der Beispiel-App

Um die Beispiel-App besser zu verstehen, können Sie sie herunterladen, selbst erstellen oder den Code am Ende dieses Themas überprüfen, ohne die App zu implementieren.

Hinweis

Um das Beispiel als Windows Presentation Foundation (WPF)-Desktop-App auszuführen, müssen Sie Visual Studio 2012 oder höher und .NET Framework 4.5 oder höher auf Ihrem Computer installiert haben.

Herunterladen der App

  1. Laden Sie die komprimierte Datei aus Async Samples herunter: Reentrancy in .NET Desktop Apps.

  2. Dekomprimieren Sie die heruntergeladene Datei, und starten Sie Dann Visual Studio.

  3. Wählen Sie auf der Menüleiste "Datei", "Öffnen", "Projekt/Projektmappe" aus.

  4. Navigieren Sie zu dem Ordner, der den dekomprimierten Beispielcode enthält, und öffnen Sie dann die Lösungsdatei (.sln).

  5. Öffnen Sie im Lösungs-Explorer das Kontextmenü für das Projekt, das Sie ausführen möchten, und wählen Sie dann "Als Startprojekt festlegen" aus.

  6. Wählen Sie die STRG+F5-Tasten aus, um das Projekt zu erstellen und auszuführen.

Erstellen der App

Der folgende Abschnitt enthält den Code zum Erstellen des Beispiels als WPF-App.

So erstellen Sie eine WPF-App
  1. Starten Sie Visual Studio.

  2. Klicken Sie in der Menüleiste auf Datei, Neu, Projekt.

    Das Dialogfeld "Neues Projekt " wird geöffnet.

  3. Erweitern Sie im Bereich "Installierte Vorlagen " Visual Basic, und erweitern Sie dann Windows.

  4. Wählen Sie in der Liste der Projekttypen WPF-Anwendung aus.

  5. Benennen Sie das Projekt WebsiteDownloadWPF, wählen Sie .NET Framework-Version von 4.6 oder höher aus, und klicken Sie dann auf die Schaltfläche "OK ".

    Das neue Projekt wird im Projektmappen-Explorer angezeigt.

  6. Wählen Sie im Visual Studio Code-Editor die Registerkarte "MainWindow.xaml " aus.

    Wenn die Registerkarte nicht sichtbar ist, öffnen Sie das Kontextmenü für "MainWindow.xaml" im Projektmappen-Explorer, und wählen Sie dann "Code anzeigen" aus.

  7. Ersetzen Sie in der XAML-Ansicht von "MainWindow.xaml" den Code durch den folgenden Code.

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWPF"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Width="517" Height="360">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="-1,0,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="53" Background="#FFA89B9B" FontSize="36" Width="518"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="-1,53,0,-36" TextWrapping="Wrap" VerticalAlignment="Top" Height="343" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="518" FontFamily="Lucida Console" />
        </Grid>
    </Window>
    

    Ein einfaches Fenster mit einem Textfeld und einer Schaltfläche wird in der Entwurfsansicht von "MainWindow.xaml" angezeigt.

  8. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf Verweise und wählen Sie "Verweis hinzufügen" aus.

    Fügen Sie einen Verweis für System.Net.Http, wenn er noch nicht ausgewählt ist.

  9. Öffnen Sie im Lösungs-Explorer das Kontextmenü für MainWindow.xaml.vb, und wählen Sie dann Code anzeigen aus.

  10. Ersetzen Sie in MainWindow.xaml.vb den Code durch den folgenden Code.

    ' Add the following Imports statements, and add a reference for System.Net.Http.
    Imports System.Net.Http
    Imports System.Threading
    
    Class MainWindow
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol Or System.Net.SecurityProtocolType.Tls12
    
            ' This line is commented out to make the results clearer in the output.
            'ResultsTextBox.Text = ""
    
            Try
                Await AccessTheWebAsync()
    
            Catch ex As Exception
                ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    
            End Try
        End Sub
    
        Private Async Function AccessTheWebAsync() As Task
    
            ' Declare an HttpClient object.
            Dim client = New HttpClient()
    
            ' Make a list of web addresses.
            Dim urlList As List(Of String) = SetUpURLList()
    
            Dim total = 0
            Dim position = 0
    
            For Each url In urlList
                ' GetByteArrayAsync returns a task. At completion, the task
                ' produces a byte array.
                Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
                position += 1
                DisplayResults(url, urlContents, position)
    
                ' Update the total.
                total += urlContents.Length
            Next
    
            ' Display the total count for all of the websites.
            ResultsTextBox.Text &=
                String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
        End Function
    
        Private Function SetUpURLList() As List(Of String)
            Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/hh191443.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/jj155761.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/hh524395.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/ff730837.aspx"
            }
            Return urls
        End Function
    
        Private Sub DisplayResults(url As String, content As Byte(), pos As Integer)
            ' Display the length of each website. The string format is designed
            ' to be used with a monospaced font, such as Lucida Console or
            ' Global Monospace.
    
            ' Strip off the "http:'".
            Dim displayURL = url.Replace("https://", "")
            ' Display position in the URL list, the URL, and the number of bytes.
            ResultsTextBox.Text &= String.Format(vbCrLf & "{0}. {1,-58} {2,8}", pos, displayURL, content.Length)
        End Sub
    End Class
    
  11. Wählen Sie die STRG+F5-Tasten aus, um das Programm auszuführen, und wählen Sie dann mehrmals die Schaltfläche "Start " aus.

  12. Nehmen Sie die Änderungen aus Die Schaltfläche „Start“ deaktivieren, Den Vorgang abbrechen und neu starten oder Mehrere Vorgänge ausführen und die Ausgabe in eine Warteschlange stellen vor, um mit Ablaufinvarianz umzugehen.

Siehe auch