Freigeben über


Verbleibende asynchrone Aufgaben nach Abschluss eines Vorgangs abbrechen (Visual Basic)

Wenn Sie die Task.WhenAny Methode zusammen mit einer CancellationTokenMethode verwenden, können Sie alle verbleibenden Vorgänge abbrechen, wenn eine Aufgabe abgeschlossen ist. Die WhenAny Methode verwendet ein Argument, das eine Sammlung von Aufgaben ist. Die Methode startet alle Vorgänge und gibt einen einzelnen Vorgang zurück. Die einzelne Aufgabe ist abgeschlossen, wenn eine Aufgabe in der Auflistung abgeschlossen ist.

In diesem Beispiel wird veranschaulicht, wie ein Abbruchtoken in Verbindung mit WhenAny verwendet wird, um an der ersten Aufgabe festzuhalten, die in der Auflistung von Aufgaben beendet wird, und die übrigen Aufgaben abzubrechen. Jede Aufgabe lädt den Inhalt einer Website herunter. Im Beispiel wird die Länge des Inhalts des ersten Downloads angezeigt, um die anderen Downloads abzuschließen und abzubrechen.

Hinweis

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

Herunterladen des Beispiels

Sie können das vollständige Windows Presentation Foundation (WPF)-Projekt aus dem Async-Beispiel herunterladen: Optimieren Sie Ihre Anwendung , und führen Sie dann die folgenden Schritte aus.

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

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

  3. Öffnen Sie im Dialogfeld "Projekt öffnen" den Ordner mit dem Beispielcode, den Sie dekomprimiert haben, und öffnen Sie dann die Lösungsdatei (.sln) für AsyncFineTuningVB.

  4. Öffnen Sie im Projektmappen-Explorer das Kontextmenü für das CancelAfterOneTask-Projekt, und wählen Sie dann Als Startprojekt festlegen aus.

  5. Wählen Sie die F5-TASTE aus, um das Projekt auszuführen.

    Wählen Sie die STRG+F5-Tasten aus, um das Projekt auszuführen, ohne es zu debuggen.

  6. Führen Sie das Programm mehrmals aus, um zu überprüfen, ob verschiedene Downloads zuerst abgeschlossen sind.

Wenn Sie das Projekt nicht herunterladen möchten, können Sie die MainWindow.xaml.vb Datei am Ende dieses Themas überprüfen.

Erstellen des Beispiels

Das Beispiel in diesem Thema baut auf dem Projekt auf, das unter Eine asynchrone Aufgabe oder Aufgabenliste abbrechen entwickelt wurde, um eine Aufgabenliste abzubrechen. Im Beispiel wird dieselbe Benutzeroberfläche verwendet, obwohl die Schaltfläche "Abbrechen " nicht explizit verwendet wird.

Um das Beispiel selbst zu erstellen, befolgen Sie schritt für Schritt die Anweisungen im Abschnitt "Herunterladen des Beispiels", wählen Sie aber CancelAListOfTasks als StartUp-Projekt aus. Fügen Sie die Änderungen in diesem Thema zu diesem Projekt hinzu.

Starten Sie in der Datei „MainWindow.xaml.vb“ des CancelAListOfTasks-Projekts den Übergang, indem Sie die Verarbeitungsschritte für jede Website von der Schleife in AccessTheWebAsync zur folgenden asynchronen Methode verschieben.

' ***Bundle the processing steps for a website into one async method.
Async Function ProcessURLAsync(url As String, client As HttpClient, ct As CancellationToken) As Task(Of Integer)

    ' GetAsync returns a Task(Of HttpResponseMessage).
    Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

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

    Return urlContents.Length
End Function

In AccessTheWebAsyncdiesem Beispiel wird eine Abfrage, die ToArray Methode und die WhenAny Methode zum Erstellen und Starten eines Arrays von Aufgaben verwendet. Die Anwendung von WhenAny auf den Array gibt eine einzelne Aufgabe zurück, die, wenn sie erwartet wird, zur ersten Aufgabe auswertet wird, die im Array von Aufgaben zum Abschluss kommt.

Nehmen Sie die folgenden Änderungen in AccessTheWebAsync. Sternchen markieren die Änderungen in der Codedatei.

  1. Kommentieren Sie die Schleife aus oder löschen Sie sie.

  2. Erstellen Sie eine Abfrage, die beim Ausführen eine Sammlung generischer Aufgaben erzeugt. Jeder Aufruf an ProcessURLAsync gibt ein Task<TResult> zurück, bei dem TResult eine ganze Zahl ist.

    ' ***Create a query that, when executed, returns a collection of tasks.
    Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
        From url In urlList Select ProcessURLAsync(url, client, ct)
    
  3. Rufen Sie ToArray auf, um die Abfrage auszuführen und die Aufgaben zu starten. Die Anwendung der WhenAny-Methode im nächsten Schritt würde die Abfrage ausführen und die Aufgaben ohne Verwendung von ToArray starten, aber andere Methoden könnten das möglicherweise nicht. Die sicherste Methode besteht darin, die Ausführung der Abfrage explizit zu erzwingen.

    ' ***Use ToArray to execute the query and start the download tasks.
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
  4. Rufen Sie WhenAny mit der Auflistung von Aufgaben auf. WhenAny gibt ein Task(Of Task(Of Integer)) oder Task<Task<int>> zurück. Das bedeutet, dass WhenAny eine Aufgabe zurückgibt, die zu einem einzelnen Task(Of Integer) oder Task<int> ausgewertet wird, wenn sie erwartet wird. Dieser einzelne Vorgang ist der erste Vorgang in der Sammlung, der abgeschlossen werden soll. Die Aufgabe, die zuerst abgeschlossen wurde, wird zugewiesen finishedTask. Der Typ von finishedTask ist Task<TResult>, wobei TResult eine ganze Zahl ist, da dies der Rückgabetyp von ProcessURLAsync ist.

    ' ***Call WhenAny and then await the result. The task that finishes
    ' first is assigned to finishedTask.
    Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
    
  5. In diesem Beispiel interessieren Sie sich nur für den Vorgang, der zuerst abgeschlossen ist. Verwenden Sie CancellationTokenSource.Cancel daher zum Abbrechen der verbleibenden Vorgänge.

    ' ***Cancel the rest of the downloads. You just want the first one.
    cts.Cancel()
    
  6. Warten Sie schließlich auf finishedTask, um die Länge des heruntergeladenen Inhalts abzurufen.

    Dim length = Await finishedTask
    resultsTextBox.Text &= vbCrLf & $"Length of the downloaded website:  {length}" & vbCrLf
    

Führen Sie das Programm mehrmals aus, um zu überprüfen, ob verschiedene Downloads zuerst abgeschlossen sind.

Vollständiges Beispiel

Der folgende Code ist die vollständige MainWindow.xaml.vb- oder MainWindow.xaml.cs Datei für das Beispiel. Sternchen markieren die Elemente, die für dieses Beispiel hinzugefügt wurden.

Beachten Sie, dass Sie einen Verweis für System.Net.Http hinzufügen müssen.

Sie können das Projekt aus dem Async-Beispiel herunterladen: Optimieren Sie Ihre Anwendung.

' Add an Imports directive and a reference for System.Net.Http.
Imports System.Net.Http

' Add the following Imports directive for System.Threading.
Imports System.Threading

Class MainWindow

    ' Declare a System.Threading.CancellationTokenSource.
    Dim cts As CancellationTokenSource

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

        ' Instantiate the CancellationTokenSource.
        cts = New CancellationTokenSource()

        resultsTextBox.Clear()

        Try
            Await AccessTheWebAsync(cts.Token)
            resultsTextBox.Text &= vbCrLf & "Download complete."

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

        Catch ex As Exception
            resultsTextBox.Text &= vbCrLf & "Download failed." & vbCrLf
        End Try

        ' Set the CancellationTokenSource to Nothing when the download is complete.
        cts = Nothing
    End Sub

    ' You can still include a Cancel button if you want to.
    Private Sub cancelButton_Click(sender As Object, e As RoutedEventArgs)

        If cts IsNot Nothing Then
            cts.Cancel()
        End If
    End Sub

    ' Provide a parameter for the CancellationToken.
    ' Change the return type to Task because the method has no return statement.
    Async Function AccessTheWebAsync(ct As CancellationToken) As Task

        Dim client As HttpClient = New HttpClient()

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

        '' Comment out or delete the loop.
        ''For Each url In urlList
        ''    ' GetAsync returns a Task(Of HttpResponseMessage).
        ''    ' Argument ct carries the message if the Cancel button is chosen.
        ''    ' Note that the Cancel button can cancel all remaining downloads.
        ''    Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

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

        ''    resultsTextBox.Text &=
        ''        vbCrLf & $"Length of the downloaded string: {urlContents.Length}." & vbCrLf
        ''Next

        ' ***Create a query that, when executed, returns a collection of tasks.
        Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
            From url In urlList Select ProcessURLAsync(url, client, ct)

        ' ***Use ToArray to execute the query and start the download tasks.
        Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()

        ' ***Call WhenAny and then await the result. The task that finishes
        ' first is assigned to finishedTask.
        Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)

        ' ***Cancel the rest of the downloads. You just want the first one.
        cts.Cancel()

        ' ***Await the first completed task and display the results
        ' Run the program several times to demonstrate that different
        ' websites can finish first.
        Dim length = Await finishedTask
        resultsTextBox.Text &= vbCrLf & $"Length of the downloaded website:  {length}" & vbCrLf
    End Function

    ' ***Bundle the processing steps for a website into one async method.
    Async Function ProcessURLAsync(url As String, client As HttpClient, ct As CancellationToken) As Task(Of Integer)

        ' GetAsync returns a Task(Of HttpResponseMessage).
        Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

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

        Return urlContents.Length
    End Function

    ' Add a method that creates a list of web addresses.
    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/hh290138.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/dd470362.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com/library/ff730837.aspx"
            }
        Return urls
    End Function

End Class

' Sample output:

' Length of the downloaded website:  158856

' Download complete.

Siehe auch