Annuler les tâches Asynch restantes lorsque l’une d’elles est terminée (Visual Basic)
Utilisez la méthode Task.WhenAny avec un CancellationToken pour annuler toutes les tâches restantes quand une tâche est terminée. La méthode WhenAny
accepte un argument qui est une collection de tâches. La méthode démarre toutes les tâches et retourne une tâche unique. Cette dernière est terminée une fois que toutes les tâches de la collection sont terminées.
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.
Notes
Pour exécuter les exemples, Visual Studio version 2012 ou ultérieure et le .NET Framework version 4.5 ou ultérieure doivent être installés sur votre ordinateur.
Téléchargement de l'exemple
Téléchargez l’intégralité du projet Windows Presentation Foundation (WPF) à partir de la page Exemple Async : réglage de votre application, puis procédez comme suit.
Décompressez le fichier que vous avez téléchargé, puis démarrez Visual Studio.
Dans la barre de menus, choisissez Fichier, Ouvrir, Projet/Solution.
Dans la boîte de dialogue Ouvrir le projet, ouvrez le dossier contenant l’exemple de code que vous avez décompressé, puis ouvrez le fichier de solution (.sln) pour AsyncFineTuningVB.
Dans l’Explorateur de solutions, ouvrez le menu contextuel du projet CancelAfterOneTask, puis choisissez Définir comme projet de démarrage.
Appuyez sur la touche F5 pour exécuter le projet.
Appuyez sur les touches Ctrl+F5 pour exécuter le projet sans le déboguer.
Exécutez le programme plusieurs fois pour vérifier que différents téléchargements se terminent en premier.
Si vous ne souhaitez pas télécharger le projet, vous pouvez passer en revue le fichier MainWindow.xaml.vb à la fin de cette rubrique.
Génération de l’exemple
L’exemple de cette rubrique s’appuie sur le projet développé dans Annuler une tâche asynchrone ou une liste de tâches pour annuler une liste de tâches. Il utilise la même interface utilisateur, bien que le bouton Annuler ne soit pas utilisé explicitement.
Pour générer vous-même l’exemple, suivez les instructions pas à pas de la section « Téléchargement de l’exemple », mais choisissez CancelAListOfTasks comme Projet de démarrage. Ajoutez les changements 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 de la boucle dans 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 AccessTheWebAsync
, cet exemple utilise une requête, la méthode ToArray et la méthode WhenAny
pour créer et démarrer un tableau de tâches. L’application de WhenAny
au tableau retourne une tâche unique qui, quand elle est attendue, prend la valeur de la première tâche terminée dans le tableau de tâches.
Dans AccessTheWebAsync
, effectuez les changements suivants. Les astérisques signalent les changements dans le fichier de code.
Commentez ou supprimez la boucle.
Créer une requête qui, une fois exécutée, génère une collection de tâches génériques. Chaque appel à
ProcessURLAsync
retourne un Task<TResult> où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)
Appelez
ToArray
pour exécuter la requête et démarrer les tâches. L’application de la méthodeWhenAny
à l’étape suivante exécute la requête et démarre les tâches sans utiliserToArray
, mais il se peut que d’autres méthodes n’y parviennent pas. L’approche la plus sûre consiste à forcer l’exécution de la requête de manière explicite.' ***Use ToArray to execute the query and start the download tasks. Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
Appelez
WhenAny
sur la collection de tâches.WhenAny
retourneTask(Of Task(Of Integer))
ouTask<Task<int>>
. Autrement dit,WhenAny
retourne une tâche qui prend la valeur d’unTask(Of Integer)
ouTask<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 assignée àfinishedTask
. Le type definishedTask
est Task<TResult>, oùTResult
est un entier car il s’agit du type de retour deProcessURLAsync
.' ***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)
Dans cet exemple, vous vous intéressez uniquement à la tâche qui se termine en premier. Par conséquent, utilisez CancellationTokenSource.Cancel pour annuler les tâches restantes.
' ***Cancel the rest of the downloads. You just want the first one. cts.Cancel()
Enfin, attendez
finishedTask
pour récupérer 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 différents téléchargements se terminent en premier.
Exemple complet
Le code suivant est le fichier MainWindow.xaml.vb ou MainWindow.xaml.cs complet de l’exemple. Des astérisques marquent les éléments 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 la page Exemple Async : réglage de 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.