Partager via


Annuler les tâches asynchrones restantes une fois qu’une tâche est terminée (Visual Basic)

En utilisant la Task.WhenAny méthode avec un CancellationToken, vous pouvez annuler toutes les tâches restantes quand une tâche est terminée. La WhenAny méthode prend un argument qui est une collection de tâches. La méthode démarre toutes les tâches et retourne une seule tâche. Une seule tâche est considérée comme terminée dès qu'une tâche de la collection est achevée.

Cet exemple montre comment utiliser un jeton d’annulation avec WhenAny pour attendre la fin de la première tâche de la collection et annuler les tâches restantes. Chaque tâche télécharge le contenu d’un site web. L'exemple affiche la longueur du contenu du premier téléchargement terminé et annule les autres téléchargements.

Remarque

Pour exécuter les exemples, vous devez disposer de Visual Studio 2012 ou version ultérieure et du .NET Framework 4.5 ou ultérieur installé sur votre ordinateur.

Téléchargement de l’exemple

Vous pouvez télécharger le projet WPF (Windows Presentation Foundation) complet à partir de l’exemple Async : ajuster votre application , puis suivre ces étapes.

  1. Décompressez le fichier que vous avez téléchargé, puis démarrez Visual Studio.

  2. Dans la barre de menus, choisissez Fichier, Ouvrir, Projet/Solution.

  3. Dans la boîte de dialogue Ouvrir un projet , ouvrez le dossier contenant l’exemple de code que vous avez décompressé, puis ouvrez le fichier de solution (.sln) pour AsyncFineTuningVB.

  4. Dans l’Explorateur de solutions, ouvrez le menu contextuel du projet CancelAfterOneTask , puis choisissez Définir comme projet de démarrage.

  5. Choisissez la clé F5 pour exécuter le projet.

    Choisissez les touches Ctrl+F5 pour exécuter le projet sans le déboguer.

  6. Exécutez le programme plusieurs fois pour vérifier que les téléchargements différents se terminent en premier.

Si vous ne souhaitez pas télécharger le projet, vous pouvez consulter le fichier MainWindow.xaml.vb à la fin de cette rubrique.

Construction de l'exemple

L’exemple de cette rubrique ajoute au projet développé dans Annuler une tâche asynchrone ou une liste de tâches pour annuler une liste de tâches. L’exemple utilise la même interface utilisateur, même si le bouton Annuler n’est pas utilisé explicitement.

Pour générer l’exemple vous-même, suivez les instructions de la section « Téléchargement de l’exemple », mais choisissez CancelAListOfTasks comme projet de démarrage. Ajoutez les modifications de cette rubrique à ce projet.

Dans le fichier MainWindow.xaml.vb du projet CancelAListOfTasks, commencez la transition en déplaçant les étapes de traitement de chaque site web, qui sont actuellement dans la boucle indiquée par AccessTheWebAsync, vers la méthode asynchrone suivante.

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

Dans AccessTheWebAsynccet exemple, cet exemple utilise une requête, la ToArray méthode et la WhenAny méthode pour créer et démarrer un tableau de tâches. L'application de WhenAny au tableau renvoie une tâche unique qui, lorsqu'elle est en attente, correspond à la première tâche à se terminer dans le tableau de tâches.

Apportez les modifications suivantes dans AccessTheWebAsync. Les astérisques marquent les modifications apportées au fichier de code.

  1. Commentez ou supprimez la boucle.

  2. Créez une requête qui, lorsqu’elle est exécutée, produit une collection de tâches génériques. Chaque appel à ProcessURLAsync renvoie un Task<TResult>TResult est un entier.

    ' ***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. Appelez ToArray pour exécuter la requête et démarrer les tâches. L’application de la WhenAny méthode à l’étape suivante exécute la requête et démarre les tâches sans utiliser ToArray, mais d’autres méthodes peuvent ne pas être utilisées. La pratique la plus sûre consiste à forcer l’exécution de la requête explicitement.

    ' ***Use ToArray to execute the query and start the download tasks.
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
  4. Appelez WhenAny sur la collection de tâches. WhenAny renvoie un Task(Of Task(Of Integer)) ou Task<Task<int>>. Autrement dit, WhenAny retourne une tâche qui prend la valeur d’un Task(Of Integer) ou Task<int> unique quand elle est attendue. Cette tâche unique est la première tâche de la collection à se terminer. La tâche terminée en premier est affectée à finishedTask. Le type de finishedTask est Task<TResult>TResult est un entier, car il s'agit du type de retour de 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. Dans cet exemple, vous êtes intéressé uniquement par la tâche qui se termine en premier. Par conséquent, utilisez cette option CancellationTokenSource.Cancel pour annuler les tâches restantes.

    ' ***Cancel the rest of the downloads. You just want the first one.
    cts.Cancel()
    
  6. Enfin, attendez que finishedTask récupère la longueur du contenu téléchargé.

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

Exécutez le programme plusieurs fois pour vérifier que les téléchargements différents se terminent en premier.

Exemple complet

Le code suivant est le fichier complet MainWindow.xaml.vb ou MainWindow.xaml.cs pour l’exemple. Les astérisques marquent les éléments qui ont été ajoutés pour cet exemple.

Notez que vous devez ajouter une référence pour System.Net.Http.

Vous pouvez télécharger le projet à partir de l’exemple Async : ajuster votre application.

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

Voir aussi