Anulowanie pozostałych zadań asynchronicznych po ich ukończeniu (Visual Basic)

Używając metody wraz z , można anulować wszystkie pozostałe zadania Task.WhenAny CancellationToken po ukończeniu jednego zadania. Metoda WhenAny przyjmuje argument, który jest kolekcją zadań. Metoda uruchamia wszystkie zadania i zwraca jedno zadanie. Pojedyncze zadanie jest ukończone po zakończeniu dowolnego zadania w kolekcji.

W tym przykładzie pokazano, jak używać tokenu anulowania w połączeniu z , aby przytrzymać pierwsze zadanie w celu zakończenia z kolekcji zadań i anulowania WhenAny pozostałych zadań. Każde zadanie pobiera zawartość witryny internetowej. W przykładzie jest wyświetlana długość zawartości pierwszego pobierania do ukończenia, a inne pliki do pobrania są anulowane.

Uwaga

Aby uruchomić przykłady, musisz mieć zainstalowany Visual Studio 2012 lub nowszą oraz .NET Framework 4.5 lub nowszą na komputerze.

Pobieranie przykładu

Możesz pobrać kompletny projekt Windows Presentation Foundation (WPF) z przykładu asynchronicznego: dostrajanie aplikacji, a następnie wykonaj następujące kroki.

  1. Zdekompresuj pobrany plik, a następnie uruchom Visual Studio.

  2. Na pasku menu wybierz pozycję Plik, Otwórz, Project/Rozwiązanie.

  3. W oknie dialogowym Project otwórz folder zawierający przykładowy kod, który został zdekompresowany, a następnie otwórz plik rozwiązania (sln) dla pliku AsyncFineTuningVB.

  4. W Eksplorator rozwiązań otwórz menu skrótów projektu CancelAfterOneTask, a następnie wybierz pozycję Ustaw jako startowy Project.

  5. Wybierz klawisz F5, aby uruchomić projekt.

    Naciśnij klawisze Ctrl+F5, aby uruchomić projekt bez debugowania.

  6. Uruchom program kilka razy, aby sprawdzić, czy różne pliki do pobrania zakończą się jako pierwsze.

Jeśli nie chcesz pobierać projektu, możesz przejrzeć plik MainWindow.xaml.vb na końcu tego tematu.

Tworzenie przykładu

Przykład w tym temacie stanowi element projektu opracowanego w temacie Anulowanie zadania asynchronicznego lub Lista zadań w celu anulowania listy zadań. W przykładzie użyto tego samego interfejsu użytkownika, chociaż przycisk Anuluj nie jest jawnie używany.

Aby samodzielnie skompilować przykład, postępuj zgodnie z instrukcjami w sekcji "Pobieranie przykładu", ale wybierz pozycję CancelAListOfTasks jako Project . Dodaj zmiany wprowadzone w tym temacie do tego projektu.

W pliku MainWindow.xaml.vb projektu CancelAListOfTasks rozpocznij przejście, przenosząc kroki przetwarzania dla każdej witryny internetowej z pętli do następującej metody AccessTheWebAsync asynchronicznej.

' ***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

W tym przykładzie użyto zapytania, metody i metody do utworzenia AccessTheWebAsync ToArray i uruchomienia WhenAny tablicy zadań. Zastosowanie funkcji do tablicy zwraca jedno zadanie, które po oczekiwaniu daje w ocenie pierwsze zadanie do ukończenia WhenAny w tablicy zadań.

W programie należy wprowadzić następujące AccessTheWebAsync zmiany. Gwiazdki oznaczają zmiany w pliku kodu.

  1. Skomentuj lub usuń pętlę.

  2. Utwórz zapytanie, które po wykonaniu tworzy kolekcję ogólnych zadań. Każde wywołanie metody ProcessURLAsync zwraca wartość typu , gdzie jest liczbą Task<TResult> TResult całkowitą.

    ' ***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. Wywołaj ToArray polecenie , aby wykonać zapytanie i uruchomić zadania. Zastosowanie metody w następnym kroku będzie wykonywać zapytanie i uruchamiać zadania bez użycia metody WhenAny , ale inne metody mogą nie być ToArray wykonywane. Najbezpieczniejszym rozwiązaniem jest jawne wymuscie wykonania zapytania.

    ' ***Use ToArray to execute the query and start the download tasks.
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
  4. Wywołaj WhenAny wywołanie w kolekcji zadań. WhenAny Zwraca wartość Task(Of Task(Of Integer)) lub Task<Task<int>> . Oznacza to, że zwraca zadanie, które daje w ocenie pojedyncze zadanie lub WhenAny Task(Of Integer) gdy jest Task<int> oczekujące. To pojedyncze zadanie jest pierwszym zadaniem w kolekcji do zakończenia. Zadanie, które zakończyło się jako pierwsze, jest przypisywane do . finishedTask Typ to finishedTask , gdzie jest liczbą Task<TResult> TResult całkowitą, ponieważ jest to zwracany typ ProcessURLAsync .

    ' ***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. W tym przykładzie interesuje Cię tylko zadanie, które kończy się jako pierwsze. W związku z tym użyj CancellationTokenSource.Cancel funkcji , aby anulować pozostałe zadania.

    ' ***Cancel the rest of the downloads. You just want the first one.
    cts.Cancel()
    
  6. Na koniec finishedTask zaczekaj na pobranie długości pobranej zawartości.

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

Uruchom program kilka razy, aby sprawdzić, czy różne pliki do pobrania zakończą się jako pierwsze.

Kompletny przykład

Poniższy kod to kompletny plik MainWindow.xaml.vb lub MainWindow.xaml.cs dla przykładu. Gwiazdki oznaczają elementy, które zostały dodane w tym przykładzie.

Zwróć uwagę, że musisz dodać odwołanie do System.Net.Http .

Projekt można pobrać z przykładu asynchronicznego: Dostrajanie aplikacji.

' 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.

Zobacz też