Gestion de la réentrance dans les applications Async (Visual Basic)Handling Reentrancy in Async Apps (Visual Basic)

Quand vous incluez du code asynchrone dans votre application, vous devez prendre en compte et éventuellement empêcher la réentrance, qui fait référence à une nouvelle entrée d'une opération asynchrone avant qu'elle soit terminée.When you include asynchronous code in your app, you should consider and possibly prevent reentrancy, which refers to reentering an asynchronous operation before it has completed. Si vous n'identifiez pas et ne gérez pas les possibilités de réentrance, les résultats peuvent être inattendus.If you don't identify and handle possibilities for reentrancy, it can cause unexpected results.

Notes

Pour exécuter l’exemple, Visual Studio version 2012 ou ultérieure et .NET Framework version 4.5 ou ultérieure doivent être installés sur votre ordinateur.To run the example, you must have Visual Studio 2012 or newer and the .NET Framework 4.5 or newer installed on your computer.

Identification de la réentranceRecognizing Reentrancy

Dans l’exemple de cette rubrique, les utilisateurs choisissent un bouton Démarrer pour lancer une application asynchrone qui télécharge une série de sites web et calcule le nombre total d’octets téléchargés.In the example in this topic, users choose a Start button to initiate an asynchronous app that downloads a series of websites and calculates the total number of bytes that are downloaded. Une version synchrone de l’exemple répondrait de la même façon quel que soit le nombre de fois où un utilisateur clique sur le bouton car, après la première fois, le thread d’interface utilisateur ignore ces événements jusqu’à ce que l’application arrête de s’exécuter.A synchronous version of the example would respond the same way regardless of how many times a user chooses the button because, after the first time, the UI thread ignores those events until the app finishes running. Dans une application asynchrone, en revanche, le thread d'interface utilisateur continue de répondre et vous pouvez entrer à nouveau l'opération asynchrone avant qu'elle soit terminée.In an asynchronous app, however, the UI thread continues to respond, and you might reenter the asynchronous operation before it has completed.

L’exemple suivant illustre la sortie attendue si l’utilisateur choisit le bouton Démarrer une seule fois.The following example shows the expected output if the user chooses the Start button only once. La liste des sites web téléchargés apparaît avec la taille, en octets, de chaque site.A list of the downloaded websites appears with the size, in bytes, of each site. Le nombre total d'octets apparaît à la fin.The total number of bytes appears at the end.

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

Toutefois, si l'utilisateur choisit le bouton plusieurs fois, le gestionnaire d'événements est appelé à plusieurs reprises et le processus de téléchargement est à nouveau entré à chaque fois.However, if the user chooses the button more than once, the event handler is invoked repeatedly, and the download process is reentered each time. Ainsi, plusieurs opérations asynchrones s'exécutent en même temps, la sortie entrelace les résultats et le nombre total d'octets est source de confusion.As a result, several asynchronous operations are running at the same time, the output interleaves the results, and the total number of bytes is confusing.

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

Vous pouvez passer en revue le code qui produit cette sortie en accédant à la fin de cette rubrique.You can review the code that produces this output by scrolling to the end of this topic. Vous pouvez faire des essais avec le code en téléchargeant la solution sur votre ordinateur local, puis en exécutant le projet WebsiteDownload ou en utilisant le code donné à la fin de cette rubrique pour créer votre propre projet. Pour plus d’informations et d’instructions, consultez Examen et exécution de l’exemple d’application.You can experiment with the code by downloading the solution to your local computer and then running the WebsiteDownload project or by using the code at the end of this topic to create your own project For more information and instructions, see Reviewing and Running the Example App.

Gestion de la réentranceHandling Reentrancy

Vous pouvez gérer la réentrance de différentes façons, selon ce que vous voulez que votre application fasse.You can handle reentrancy in a variety of ways, depending on what you want your app to do. Cette rubrique présente les exemples suivants :This topic presents the following examples:

Désactiver le bouton DémarrerDisable the Start Button

Vous pouvez bloquer le bouton Démarrer pendant l’exécution d’une opération en désactivant le bouton en haut du gestionnaire d’événements StartButton_Click.You can block the Start button while an operation is running by disabling the button at the top of the StartButton_Click event handler. Ensuite, vous pouvez réactiver le bouton depuis un bloc Finally quand l'opération se termine pour que les utilisateurs puissent exécuter à nouveau l'application.You can then reenable the button from within a Finally block when the operation finishes so that users can run the app again.

Le code suivant illustre ces modifications, marquées par des astérisques.The following code shows these changes, which are marked with asterisks. Vous pouvez ajouter les modifications apportées au code à la fin de cette rubrique, ou vous pouvez télécharger l’application finie à partir des exemples Async : Reentrancy in .NET Desktop Apps (Exemples Async : réentrance dans les applications de bureau .NET).You can add the changes to the code at the end of this topic, or you can download the finished app from Async Samples: Reentrancy in .NET Desktop Apps. Le nom du projet est DisableStartButton.The project name is 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

Suite aux modifications, le bouton ne répond pas pendant que AccessTheWebAsync télécharge les sites Web, donc le processus ne peut pas être entré de nouveau.As a result of the changes, the button doesn't respond while AccessTheWebAsync is downloading the websites, so the process can’t be reentered.

Annuler et redémarrer l’opérationCancel and Restart the Operation

Au lieu de désactiver le bouton Démarrer, vous pouvez garder le bouton actif. Toutefois, si l’utilisateur choisit de nouveau ce bouton, annulez l’opération qui est déjà en cours d’exécution et laissez continuer l’opération le plus récemment démarrée.Instead of disabling the Start button, you can keep the button active but, if the user chooses that button again, cancel the operation that's already running and let the most recently started operation continue.

Pour plus d’informations sur l’annulation, consultez réglage de votre application Async (Visual Basic).For more information about cancellation, see Fine-Tuning Your Async Application (Visual Basic).

Pour configurer ce scénario, apportez les modifications suivantes au code de base fourni dans Examen et exécution de l’exemple d’application.To set up this scenario, make the following changes to the basic code that is provided in Reviewing and Running the Example App. Vous pouvez également télécharger l’application finalisée à partir de la page Async Samples: Reentrancy in .NET Desktop Apps (Exemples Async : réentrance dans les applications de bureau .NET).You also can download the finished app from Async Samples: Reentrancy in .NET Desktop Apps. Le nom de ce projet est CancelAndRestart.The name of this project is CancelAndRestart.

  1. Déclarez une variable CancellationTokenSource, cts, qui est dans la portée de toutes les méthodes.Declare a CancellationTokenSource variable, cts, that’s in scope for all methods.

    Class MainWindow // Or Class MainPage
    
        ' *** Declare a System.Threading.CancellationTokenSource.
        Dim cts As CancellationTokenSource
    
  2. Dans StartButton_Click, déterminez si une opération est déjà en cours d'exécution.In StartButton_Click, determine whether an operation is already underway. Si la valeur de cts est Nothing, aucune opération n’est déjà active.If the value of cts is Nothing, no operation is already active. Si la valeur n’est pas Nothing, l’opération qui est déjà en cours d’exécution est annulée.If the value isn't Nothing, the operation that is already running is canceled.

    ' *** If a download process is already underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If
    
  3. Définissez cts sur une autre valeur qui représente le processus en cours.Set cts to a different value that represents the current process.

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS
    
  4. À la fin de StartButton_Click, le processus actuel est terminé, donc réaffectez la valeur de cts à Nothing.At the end of StartButton_Click, the current process is complete, so set the value of cts back to Nothing.

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

Le code suivant illustre toutes les modifications dans StartButton_Click :The following code shows all the changes in StartButton_Click. Les ajouts sont marqués par des astérisques.The additions are marked with asterisks.

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

Dans AccessTheWebAsync, apportez les modifications suivantes.In AccessTheWebAsync, make the following changes.

  • Ajoutez un paramètre pour accepter le jeton d'annulation en provenance de StartButton_Click.Add a parameter to accept the cancellation token from StartButton_Click.

  • Utilisez la méthode GetAsync pour télécharger les sites Web car GetAsync accepte un argument CancellationToken.Use the GetAsync method to download the websites because GetAsync accepts a CancellationToken argument.

  • Avant d'appeler DisplayResults pour afficher les résultats de chaque site Web téléchargé, vérifiez ct pour vous assurer que l'opération en cours n'a pas été annulée.Before calling DisplayResults to display the results for each downloaded website, check ct to verify that the current operation hasn’t been canceled.

Le code suivant illustre ces modifications, marquées par des astérisques.The following code shows these changes, which are marked with asterisks.

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

Si vous choisissez le bouton Démarrer plusieurs fois pendant l’exécution de cette application, elle doit produire des résultats qui ressemblent à la sortie suivante :If you choose the Start button several times while this app is running, it should produce results that resemble the following output:

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

Pour éliminer les listes partielles, supprimez les commentaires de la première ligne de code dans StartButton_Click pour effacer la zone de texte chaque fois que l'utilisateur redémarre l'opération.To eliminate the partial lists, uncomment the first line of code in StartButton_Click to clear the text box each time the user restarts the operation.

Exécuter plusieurs opérations et mettre la sortie en file d’attenteRun Multiple Operations and Queue the Output

Ce troisième exemple est le plus compliqué, car l’application démarre une autre opération asynchrone chaque fois que l’utilisateur choisit le bouton Démarrer, et toutes les opérations s’exécutent jusqu’à leur achèvement.This third example is the most complicated in that the app starts another asynchronous operation each time that the user chooses the Start button, and all the operations run to completion. Toutes les opérations demandées téléchargent des sites web de la liste de façon asynchrone, mais la sortie des opérations est présentée séquentiellement.All the requested operations download websites from the list asynchronously, but the output from the operations is presented sequentially. Autrement dit, l’activité de téléchargement réelle est entrelacée, comme le montre la sortie dans Identification de la réentrance, mais la liste des résultats de chaque groupe est présentée séparément.That is, the actual downloading activity is interleaved, as the output in Recognizing Reentrancy shows, but the list of results for each group is presented separately.

Les opérations partagent un Task global, pendingWork, qui sert d'opérateur de contrôle d'appels pour le processus d'affichage.The operations share a global Task, pendingWork, which serves as a gatekeeper for the display process.

Vous pouvez exécuter cet exemple en collant les modifications dans le code indiqué dans Génération de l’application, ou vous pouvez suivre les instructions données dans Téléchargement de l’application pour télécharger l’exemple, puis exécuter le projet QueueResults.You can run this example by pasting the changes into the code in Building the App, or you can follow the instructions in Downloading the App to download the sample and then run the QueueResults project.

La sortie suivante montre le résultat obtenu si l’utilisateur choisit le bouton Démarrer une seule fois.The following output shows the result if the user chooses the Start button only once. L’étiquette A indique que le résultat part de la première fois que le bouton Démarrer est choisi.The letter label, A, indicates that the result is from the first time the Start button is chosen. Les numéros indiquent l'ordre des URL dans la liste des cibles de téléchargement.The numbers show the order of the URLs in the list of download targets.

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

Si l’utilisateur choisit le bouton Démarrer trois fois, l’application produit une sortie semblable aux lignes suivantes.If the user chooses the Start button three times, the app produces output that resembles the following lines. Les lignes d'information qui commencent par un signe dièse (#) suivent la progression de l'application.The information lines that start with a pound sign (#) trace the progress of the application.

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

Les groupes B et C démarrent avant que le groupe A ait terminé, mais la sortie de chaque groupe apparaît séparément.Groups B and C start before group A has finished, but the output for the each group appears separately. Toute la sortie du groupe A apparaît en premier, suivie de toute la sortie du groupe B, puis de toute la sortie du groupe C. L’application affiche toujours les groupes dans l’ordre et, pour chaque groupe, elle affiche toujours les informations sur les sites web, dans l’ordre où les URL apparaissent dans la liste des URL.All the output for group A appears first, followed by all the output for group B, and then all the output for group C. The app always displays the groups in order and, for each group, always displays the information about the individual websites in the order that the URLs appear in the list of URLs.

Toutefois, vous ne pouvez pas prévoir l'ordre dans lequel les téléchargements se produisent réellement.However, you can't predict the order in which the downloads actually happen. Après le démarrage de plusieurs groupes, les tâches de téléchargement qu'ils génèrent sont toutes actives.After multiple groups have been started, the download tasks that they generate are all active. Vous ne pouvez pas supposer que A-1 sera téléchargé avant B-1 ni que A-1 sera téléchargé avant A-2.You can't assume that A-1 will be downloaded before B-1, and you can't assume that A-1 will be downloaded before A-2.

Définitions globalesGlobal Definitions

L'exemple de code contient les deux déclarations globales suivantes qui sont visibles à partir de toutes les méthodes.The sample code contains the following two global declarations that are visible from all methods.

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)

La variable Task, pendingWork, supervise le processus d'affichage et empêche tout groupe d'interrompre l'opération d'affichage d'un autre groupe.The Task variable, pendingWork, oversees the display process and prevents any group from interrupting another group's display operation. La variable de caractère group, étiquette la sortie de différents groupes pour vérifier que les résultats apparaissent dans l'ordre attendu.The character variable, group, labels the output from different groups to verify that results appear in the expected order.

Gestionnaire d'événements ClickThe Click Event Handler

Le gestionnaire d’événements, StartButton_Click, incrémente la lettre du groupe chaque fois que l’utilisateur choisit le bouton Démarrer.The event handler, StartButton_Click, increments the group letter each time the user chooses the Start button. Ensuite, le gestionnaire appelle AccessTheWebAsync pour exécuter l'opération de téléchargement.Then the handler calls AccessTheWebAsync to run the downloading operation.

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

Méthode AccessTheWebAsyncThe AccessTheWebAsync Method

Cet exemple fractionne AccessTheWebAsync en deux méthodes.This example splits AccessTheWebAsync into two methods. La première méthode, AccessTheWebAsync, démarre toutes les tâches de téléchargement d'un groupe et configure pendingWork pour contrôler le processus d'affichage.The first method, AccessTheWebAsync, starts all the download tasks for a group and sets up pendingWork to control the display process. La méthode utilise une requête LINQ (Language Integrated Query) et ToArray pour démarrer toutes les tâches de téléchargement en même temps.The method uses a Language Integrated Query (LINQ query) and ToArray to start all the download tasks at the same time.

AccessTheWebAsync appelle ensuite FinishOneGroupAsync pour attendre la fin de chaque téléchargement et en afficher la longueur.AccessTheWebAsync then calls FinishOneGroupAsync to await the completion of each download and display its length.

FinishOneGroupAsync retourne une tâche assignée à pendingWork dans AccessTheWebAsync.FinishOneGroupAsync returns a task that's assigned to pendingWork in AccessTheWebAsync. Cette valeur empêche toute interruption par une autre opération avant que la tâche ne soit terminée.That value prevents interruption by another operation before the task is complete.

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

Méthode FinishOneGroupAsyncThe FinishOneGroupAsync Method

Cette méthode parcourt les tâches de téléchargement dans un groupe, en attendant chacune d’elles, en affichant la longueur du site web téléchargé et en ajoutant la longueur au total.This method cycles through the download tasks in a group, awaiting each one, displaying the length of the downloaded website, and adding the length to the total.

La première instruction dans FinishOneGroupAsync utilise pendingWork pour vérifier que l’entrée de la méthode n’interfère pas avec une opération qui est déjà dans le processus d’affichage ou qui est déjà en train d’attendre.The first statement in FinishOneGroupAsync uses pendingWork to make sure that entering the method doesn't interfere with an operation that is already in the display process or that's already waiting. Si une telle opération est en cours, l'opération d'entrée doit attendre son tour.If such an operation is in progress, the entering operation must wait its turn.

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

Vous pouvez exécuter cet exemple en collant les modifications dans le code indiqué dans Génération de l’application, ou vous pouvez suivre les instructions données dans Téléchargement de l’application pour télécharger l’exemple, puis exécuter le projet QueueResults.You can run this example by pasting the changes into the code in Building the App, or you can follow the instructions in Downloading the App to download the sample, and then run the QueueResults project.

Points d'intérêtPoints of Interest

Les lignes d'information qui commencent par un signe dièse (#) dans la sortie clarifient le fonctionnement de cet exemple.The information lines that start with a pound sign (#) in the output clarify how this example works.

La sortie affiche les modèles suivants.The output shows the following patterns.

  • Un groupe peut être démarré pendant qu'un groupe précédent affiche sa sortie, mais l'affichage de la sortie du groupe précédent n'est pas interrompu.A group can be started while a previous group is displaying its output, but the display of the previous group's output isn't interrupted.

    #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
    
  • La tâche pendingWork est Nothing au début de FinishOneGroupAsync uniquement pour le groupe A, qui a démarré en premier.The pendingWork task is Nothing at the start of FinishOneGroupAsync only for group A, which started first. Le groupe A n'a pas encore terminé une expression await quand il atteint FinishOneGroupAsync.Group A hasn’t yet completed an await expression when it reaches FinishOneGroupAsync. Par conséquent, le contrôle n'a pas retourné à AccessTheWebAsync, et la première assignation à pendingWork ne s'est pas produite.Therefore, control hasn't returned to AccessTheWebAsync, and the first assignment to pendingWork hasn't occurred.

  • Les deux lignes suivantes apparaissent toujours ensemble dans la sortie.The following two lines always appear together in the output. Le code n'est jamais interrompu entre le démarrage d'une opération de groupe dans StartButton_Click et l'affectation d'une tâche pour le groupe à pendingWork.The code is never interrupted between starting a group's operation in StartButton_Click and assigning a task for the group to pendingWork.

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

    Une fois qu'un groupe entre StartButton_Click, l'opération ne termine pas une expression await tant que l'opération n'entre pas FinishOneGroupAsync.After a group enters StartButton_Click, the operation doesn't complete an await expression until the operation enters FinishOneGroupAsync. Par conséquent, aucune autre opération ne peut obtenir le contrôle au cours de ce segment de code.Therefore, no other operation can gain control during that segment of code.

Examen et exécution de l’exemple d’applicationReviewing and Running the Example App

Pour mieux comprendre l’exemple d’application, vous pouvez le télécharger, le créer vous-même ou passer en revue le code à la fin de cette rubrique sans implémenter l’application.To better understand the example app, you can download it, build it yourself, or review the code at the end of this topic without implementing the app.

Notes

Pour exécuter l’exemple comme une application de bureau WPF, Visual Studio version 2012, ou une version ultérieure, et .NET Framework version 4.5, ou une version ultérieure, doivent être installés sur votre ordinateur.To run the example as a Windows Presentation Foundation (WPF) desktop app, you must have Visual Studio 2012 or newer and the .NET Framework 4.5 or newer installed on your computer.

Téléchargement de l’applicationDownloading the App

  1. Téléchargez le fichier compressé à partir de la page Async Samples: Reentrancy in .NET Desktop Apps (Exemples Async : réentrance dans les applications de bureau .NET).Download the compressed file from Async Samples: Reentrancy in .NET Desktop Apps.

  2. Décompressez le fichier que vous avez téléchargé, puis démarrez Visual Studio.Decompress the file that you downloaded, and then start Visual Studio.

  3. Dans la barre de menus, choisissez Fichier, Ouvrir, Projet/Solution.On the menu bar, choose File, Open, Project/Solution.

  4. Accédez au dossier qui contient l’exemple de code décompressé, puis ouvrez le fichier solution (.sln).Navigate to the folder that holds the decompressed sample code, and then open the solution (.sln) file.

  5. Dans l’Explorateur de solutions, ouvrez le menu contextuel du projet à modifier, puis choisissez Définir comme StartUpProject.In Solution Explorer, open the shortcut menu for the project that you want to run, and then choose Set as StartUpProject.

  6. Appuyez sur les touches CTRL+F5 pour générer et exécuter le projet.Choose the CTRL+F5 keys to build and run the project.

Génération de l’applicationBuilding the App

La section suivante fournit le code pour générer l’exemple comme une application WPF.The following section provides the code to build the example as a WPF app.

Pour générer une application WPFTo build a WPF app
  1. Démarrez Visual Studio.Start Visual Studio.

  2. Dans la barre de menus, sélectionnez Fichier, Nouveau, Projet.On the menu bar, choose File, New, Project.

    La boîte de dialogue Nouveau projet s'affiche.The New Project dialog box opens.

  3. Dans le volet modèles installés , développez Visual Basic, puis développez Windows.In the Installed Templates pane, expand Visual Basic, and then expand Windows.

  4. Dans la liste des types de projets, choisissez Application WPF.In the list of project types, choose WPF Application.

  5. Nommez le projet WebsiteDownloadWPF, puis cliquez sur le bouton OK.Name the project WebsiteDownloadWPF, and then choose the OK button.

    Le nouveau projet s’affiche dans l’Explorateur de solutions.The new project appears in Solution Explorer.

  6. Dans l'éditeur de code Visual Studio, choisissez l'onglet MainWindow.xaml .In the Visual Studio Code Editor, choose the MainWindow.xaml tab.

    Si l’onglet n’est pas visible, ouvrez le menu contextuel de MainWindow.xaml dans l’Explorateur de solutions, puis choisissez Afficher le code.If the tab isn’t visible, open the shortcut menu for MainWindow.xaml in Solution Explorer, and then choose View Code.

  7. Dans la vue XAML de MainWindow.xaml, remplacez le code par le code suivant.In the XAML view of MainWindow.xaml, replace the code with the following 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>
    

    Une fenêtre simple contenant une zone de texte et un bouton apparaît dans la vue Design de MainWindow.xaml.A simple window that contains a text box and a button appears in the Design view of MainWindow.xaml.

  8. Ajoutez une référence pour System.Net.Http.Add a reference for System.Net.Http.

  9. Dans Explorateur de solutions, ouvrez le menu contextuel de MainWindow. Xaml. vb, puis choisissez afficher le code.In Solution Explorer, open the shortcut menu for MainWindow.xaml.vb, and then choose View Code.

  10. Dans MainWindow. Xaml. vb, remplacez le code par le code suivant.In MainWindow.xaml.vb , replace the code with the following 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)
            ' 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. Appuyez sur les touches CTRL+F5 pour exécuter le programme, puis choisissez le bouton Démarrer plusieurs fois.Choose the CTRL+F5 keys to run the program, and then choose the Start button several times.

  12. Apportez les modifications indiquées dans Désactiver le bouton Démarrer, Annuler et redémarrer l’opération ou Exécuter plusieurs opérations et mettre la sortie en file d’attente pour gérer la réentrance.Make the changes from Disable the Start Button, Cancel and Restart the Operation, or Run Multiple Operations and Queue the Output to handle the reentrancy.

Voir aussiSee also