Partager via


Procédure pas à pas : création d'un composant simple multithread avec Visual Basic

Le composant BackgroundWorker remplace l'espace de noms System.Threading et lui ajoute des fonctionnalités ; toutefois, l'espace de noms System.Threading est conservé pour la compatibilité descendante et une utilisation ultérieure, si tel est votre choix. Pour plus d'informations, consultez Vue d'ensemble du composant BackgroundWorker.

Vous pouvez écrire des applications capables d'effectuer plusieurs tâches simultanément. Cette possibilité, appelée modèle multithread ou modèle de thread libre, est un moyen puissant de concevoir des composants sollicitant beaucoup le processeur et nécessitant une entrée d'utilisateur. Par exemple, un composant calculant des informations relatives aux salaires peut utiliser le multithreading. Ce composant peut traiter les données entrées dans une base de données par un utilisateur sur un thread tout en effectuant les calculs des salaires, qui sollicitent beaucoup le processeur, sur un autre thread. Grâce à l'exécution de ces processus sur des threads distincts, les utilisateurs n'ont pas besoin d'attendre que l'ordinateur ait terminé les calculs pour entrer de nouvelles données. Dans cette procédure pas à pas, vous allez créer un composant simple multithread exécutant simultanément plusieurs calculs complexes.

Création du projet

L'application est constituée d'un formulaire unique et d'un composant. L'utilisateur entre des valeurs et indique au composant qu'il peut commencer les calculs. Le formulaire reçoit ensuite des valeurs du composant et les affiche dans des contrôles Label. Le composant exécute les calculs sollicitant le processeur et indique au formulaire la fin de ces calculs. Vous allez créer des variables publiques dans votre composant pour y stocker les valeurs reçues de l'interface utilisateur. Vous allez également implémenter des méthodes dans votre composant afin d'effectuer les calculs en fonction des valeurs de ces variables.

Notes

Bien qu'il soit généralement préférable d'utiliser une fonction pour une méthode qui calcule une valeur, il n'est pas possible de passer les arguments entre threads, ni de retourner des valeurs. Il existe plusieurs moyens simples de fournir des valeurs aux threads et de recevoir des valeurs de ces threads. Dans cette démonstration, vous allez retourner des valeurs à l'interface utilisateur en mettant à jour des variables publiques ; des événements seront utilisés pour informer le programme principal de la fin de l'exécution d'un thread.

Les boîtes de dialogue et les commandes de menu qui s'affichent peuvent être différentes de celles qui sont décrites dans l'aide, en fonction de vos paramètres actifs ou de l'édition utilisée. Pour modifier vos paramètres, choisissez Importation et exportation de paramètres dans le menu Outils. Pour plus d'informations, consultez Utilisation des paramètres.

Pour créer le formulaire

  1. Créez un nouveau projet Application Windows.

  2. Nommez l'application Calculations et renommez Form1.vb comme frmCalculations.vb.

  3. Lorsque Visual Studio vous invite à renommer l'élément de code Form1, cliquez sur Oui.

    Ce formulaire servira d'interface utilisateur principale pour votre application.

  4. Ajoutez à votre formulaire cinq contrôles Label, quatre contrôles Button et un contrôle TextBox.

    Contrôle

    Nom

    Texte

    Label1

    lblFactorial1

    (vide)

    Label2

    lblFactorial2

    (vide)

    Label3

    lblAddTwo

    (vide)

    Label4

    lblRunLoops

    (vide)

    Label5

    lblTotalCalculations

    (vide)

    Button1

    btnFactorial1

    Factorial

    Button2

    btnFactorial2

    Factorial - 1

    Button3

    btnAddTwo

    Add Two

    Button4

    btnRunLoops

    Run a Loop

    TextBox1

    txtValue

    (vide)

Pour créer le composant Calculator

  1. Dans le menu Projet, choisissez Ajouter un composant.

  2. Nommez ce composant : Calculator.

Pour ajouter des variables publiques au composant Calculator

  1. Ouvrez l'éditeur de code pour Calculator.

  2. Ajoutez des instructions pour créer des variables publiques qui seront utilisées pour passer les valeurs de frmCalculations à chaque thread.

    La variable varTotalCalculations conserve en permanence le total du nombre de calculs effectués par le composant, l'autre variable recevant les valeurs du formulaire.

    Public varAddTwo As Integer
    Public varFact1 As Integer
    Public varFact2 As Integer
    Public varLoopValue As Integer
    Public varTotalCalculations As Double = 0
    

Pour ajouter des méthodes et des événements au composant Calculator

  1. Déclarez les événements que votre composant utilisera pour communiquer des valeurs à votre formulaire. Tapez le code suivant juste en dessous des déclarations de variable entrées lors de l'étape précédente :

    Public Event FactorialComplete(ByVal Factorial As Double, ByVal _
       TotalCalculations As Double)
    Public Event FactorialMinusComplete(ByVal Factorial As Double, ByVal _
       TotalCalculations As Double)
    Public Event AddTwoComplete(ByVal Result As Integer, ByVal _
       TotalCalculations As Double)
    Public Event LoopComplete(ByVal TotalCalculations As Double, ByVal _
       Counter As Integer)
    
  2. Tapez le code suivant juste en dessous des déclarations de variable entrées lors de l'étape 1 :

    ' This sub will calculate the value of a number minus 1 factorial 
    ' (varFact2-1!).
    Public Sub FactorialMinusOne()
       Dim varX As Integer = 1
       Dim varTotalAsOfNow As Double
       Dim varResult As Double = 1
       ' Performs a factorial calculation on varFact2 - 1.
       For varX = 1 to varFact2 - 1
          varResult *= varX
          ' Increments varTotalCalculations and keeps track of the current
          ' total as of this instant.
          varTotalCalculations += 1
          varTotalAsOfNow = varTotalCalculations
       Next varX
       ' Signals that the method has completed, and communicates the 
       ' result and a value of total calculations performed up to this 
       ' point
       RaiseEvent FactorialMinusComplete(varResult, varTotalAsOfNow)
    End Sub
    
    ' This sub will calculate the value of a number factorial (varFact1!).
    Public Sub Factorial()
       Dim varX As Integer = 1
       Dim varResult As Double = 1
       Dim varTotalAsOfNow As Double = 0
       For varX = 1 to varFact1
           varResult *= varX
           varTotalCalculations += 1
           varTotalAsOfNow = varTotalCalculations
       Next varX
       RaiseEvent FactorialComplete(varResult, varTotalAsOfNow)
    End Sub
    
    ' This sub will add two to a number (varAddTwo + 2).
    Public Sub AddTwo()
       Dim varResult As Integer
       Dim varTotalAsOfNow As Double
       varResult = varAddTwo + 2
       varTotalCalculations += 1
       varTotalAsOfNow = varTotalCalculations
       RaiseEvent AddTwoComplete(varResult, varTotalAsOfNow)
    End Sub
    
    ' This method will run a loop with a nested loop varLoopValue times.
    Public Sub RunALoop()
       Dim varX As Integer
       Dim varY As Integer
       Dim varTotalAsOfNow As Double
       For varX = 1 To varLoopValue
          ' This nested loop is added solely for the purpose of slowing
          ' down the program and creating a processor-intensive
          ' application.
          For varY = 1 To 500
             varTotalCalculations += 1
             varTotalAsOfNow = varTotalCalculations
          Next
       Next
       RaiseEvent LoopComplete(varTotalAsOfNow, varX - 1)
    End Sub
    

Transfert de l'entrée d'utilisateur vers le composant

L'étape suivante consiste à ajouter du code à frmCalculations afin de recevoir des entrées de l'utilisateur et d'échanger des valeurs avec le composant Calculator.

Pour implémenter des fonctionnalités client dans frmCalculations

  1. Sélectionnez Générer la solution dans le menu Générer.

  2. Ouvrez frmCalculations dans le Concepteur Windows Forms.

  3. Localisez l'onglet Composants de calculs dans la boîte à outils. Faites glisser un composant Calculator sur l'aire de conception.

  4. Dans la fenêtre Propriétés, cliquez sur le bouton Événements.

  5. Double-cliquez sur chacun des quatre événements pour créer des gestionnaires d'événements dans frmCalculations. Vous devrez retourner au concepteur après que chaque gestionnaire d'événements a été créé.

  6. Insérez le code suivant pour gérer les événements que votre formulaire recevra de Calculator1 :

    Private Sub Calculator1_AddTwoComplete(ByVal Result As System.Int32, ByVal TotalCalculations As System.Double) Handles Calculator1.AddTwoComplete
        lblAddTwo.Text = Result.ToString
        btnAddTwo.Enabled = True
        lblTotalCalculations.Text = "TotalCalculations are " & _
            TotalCalculations.ToString
    End Sub
    
    Private Sub Calculator1_FactorialComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialComplete
        ' Displays the returned value in the appropriate label.
        lblFactorial1.Text = Factorial.ToString
        ' Re-enables the button so it can be used again.
        btnFactorial1.Enabled = True
        ' Updates the label that displays the total calculations performed
        lblTotalCalculations.Text = "TotalCalculations are " & _
           TotalCalculations.ToString
    End Sub
    
    Private Sub Calculator1_FactorialMinusComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialMinusComplete
        lblFactorial2.Text = Factorial.ToString
        btnFactorial2.Enabled = True
        lblTotalCalculations.Text = "TotalCalculations are " & _
            TotalCalculations.ToString
    End Sub
    
    Private Sub Calculator1_LoopComplete(ByVal TotalCalculations As System.Double, ByVal Counter As System.Int32) Handles Calculator1.LoopComplete
        btnRunLoops.Enabled = True
        lblRunLoops.Text = Counter.ToString
        lblTotalCalculations.Text = "TotalCalculations are " & _
           TotalCalculations.ToString
    End Sub
    
  7. Recherchez l'instruction End Class en bas de l'éditeur de code. Juste au-dessus, ajoutez le code suivant pour gérer les clics de boutons :

    Private Sub btnFactorial1_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnFactorial1.Click
       ' Passes the value typed in the txtValue to Calculator.varFact1.
       Calculator1.varFact1 = CInt(txtValue.Text)
       ' Disables the btnFactorial1 until this calculation is complete.
       btnFactorial1.Enabled = False
       Calculator1.Factorial()
    End Sub
    
    Private Sub btnFactorial2_Click(ByVal sender As Object, ByVal e _
       As System.EventArgs) Handles btnFactorial2.Click
       Calculator1.varFact2 = CInt(txtValue.Text)
       btnFactorial2.Enabled = False
       Calculator1.FactorialMinusOne()
    End Sub
    
    Private Sub btnAddTwo_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnAddTwo.Click
       Calculator1.varAddTwo = CInt(txtValue.Text)
       btnAddTwo.Enabled = False
       Calculator1.AddTwo()
    End Sub
    
    Private Sub btnRunLoops_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnRunLoops.Click
       Calculator1.varLoopValue = CInt(txtValue.Text)
       btnRunLoops.Enabled = False
       ' Lets the user know that a loop is running.
       lblRunLoops.Text = "Looping"
       Calculator1.RunALoop()
    End Sub
    

Test de l'application

Vous venez de créer un projet qui intègre un formulaire et un composant capable d'effectuer plusieurs calculs complexes. Bien que vous n'ayez pas encore implémenté la fonctionnalité de multithreading, vous allez tester votre projet afin de vérifier ses fonctionnalités avant de continuer.

Pour tester le projet

  1. Dans le menu Déboguer, cliquez sur Démarrer le débogage. L'application démarre et frmCalculations apparaît.

  2. Dans la zone de texte, tapez 4, puis cliquez sur le bouton intitulé Add Two.

    Le chiffre "6" doit s'afficher dans l'étiquette située sous ce bouton et le texte "Total Calculations are 1" doit s'afficher dans lblTotalCalculations.

  3. Cliquez à présent sur le bouton intitulé Factorial - 1.

    Le chiffre "6" doit s'afficher dans l'étiquette située sous le bouton ; lblTotalCalculations affiche à présent "Total Calculations are 4".

  4. Remplacez la valeur de la zone de texte par 20, puis cliquez sur le bouton intitulé Factorial.

    Le nombre "2.43290200817664E+18" s'affiche dans l'étiquette située sous ce bouton ; lblTotalCalculations affiche à présent "Total Calculations are 24".

  5. Remplacez la valeur de la zone de texte par 50 000, puis cliquez sur le bouton Run A Loop.

    Remarquez que ce bouton n'est réactivé qu'après un instant bref mais perceptible. L'étiquette située sous ce bouton doit afficher "50000" et le nombre total de calculs affiche "25000024".

  6. Remplacez la valeur de la zone de texte par 5 000 000, cliquez sur le bouton intitulé Run A Loop, puis cliquez immédiatement sur le bouton intitulé Add Two. Cliquez de nouveau sur Add Two.

    Le bouton ne répond pas, pas plus que les contrôles du formulaire, tant que les boucles ne sont pas terminées.

    Si votre programme exécute un seul thread d'exécution, les calculs sollicitant beaucoup le processeur, comme ceux de l'exemple ci-dessus, ont tendance à bloquer le programme jusqu'à ce qu'ils soient terminés. Dans la section suivante, vous allez ajouter des fonctionnalités de multithreading à votre application afin de permettre l'exécution simultanée de plusieurs threads.

Ajout de la fonctionnalité de multithreading

L'exemple précédent a permis d'illustrer les limites des applications n'exécutant qu'un seul thread d'exécution. Dans la section suivante, vous utiliserez la classe Thread pour ajouter plusieurs threads d'exécution à votre composant.

Pour ajouter la sous-routine Threads

  1. Ouvrez Calculator.vb dans l'éditeur de code. Vers le haut du code, recherchez la ligne Public Class Calculator . Juste en dessous de cette ligne, tapez ceci :

    ' Declares the variables you will use to hold your thread objects.
    Public FactorialThread As System.Threading.Thread
    Public FactorialMinusOneThread As System.Threading.Thread
    Public AddTwoThread As System.Threading.Thread
    Public LoopThread As System.Threading.Thread
    
  2. Juste avant l'instruction End Class, en bas du code, ajoutez la méthode suivante :

    Public Sub ChooseThreads(ByVal threadNumber As Integer)
    ' Determines which thread to start based on the value it receives.
       Select Case threadNumber
          Case 1
             ' Sets the thread using the AddressOf the subroutine where
             ' the thread will start.
             FactorialThread = New System.Threading.Thread(AddressOf _
                Factorial)
             ' Starts the thread.
             FactorialThread.Start()
          Case 2
             FactorialMinusOneThread = New _
                System.Threading.Thread(AddressOf FactorialMinusOne)
             FactorialMinusOneThread.Start()
          Case 3
             AddTwoThread = New System.Threading.Thread(AddressOf AddTwo)
             AddTwoThread.Start()
          Case 4
             LoopThread = New System.Threading.Thread(AddressOf RunALoop)
             LoopThread.Start()
       End Select
    End Sub
    

    Lorsqu'un objet Thread est instancié, il requiert un argument sous la forme d'un objet ThreadStart. L'objet ThreadStart est un délégué qui pointe sur l'adresse de la sous-routine où le thread doit commencer. Un objet ThreadStart ne peut pas accepter de paramètres ni passer de valeurs. Par conséquent, il ne peut pas indiquer une fonction. L'opérateur Opérateur AddressOf (Visual Basic) retourne un délégué qui sert d'objet ThreadStart. Le sub ChooseThreads que vous venez d'implémenter reçoit une valeur du programme qui l'appelle et utilise cette valeur pour déterminer le thread à démarrer.

Pour ajouter le code de démarrage de thread à frmCalculations

  1. Ouvrez le fichier frmCalculations.vb dans l'éditeur de code. Localisez Sub btnFactorial1_Click.

    1. Commentez la ligne appelant la méthode Calculator1.Factorial directement comme suit :

      ' Calculator1.Factorial
      
    2. Ajoutez la ligne suivante afin d'appeler la méthode Calculator1.ChooseThreads :

      ' Passes the value 1 to Calculator1, thus directing it to start the ' correct thread.
      Calculator1.ChooseThreads(1)
      
  2. Modifiez de la même façon les autres sous-routines button_click.

    Notes

    Prenez soin d'inclure la valeur appropriée pour l'argument threads.

    Lorsque vous avez terminé, votre code doit ressembler au code suivant :

    Private Sub btnFactorial1_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnFactorial1.Click
       ' Passes the value typed in the txtValue to Calculator.varFact1.
       Calculator1.varFact1 = CInt(txtValue.Text)
       ' Disables the btnFactorial1 until this calculation is complete.
       btnFactorial1.Enabled = False
       ' Calculator1.Factorial()
       ' Passes the value 1 to Calculator1, thus directing it to start the
       ' Correct thread.
       Calculator1.ChooseThreads(1)
    End Sub
    
    Private Sub btnFactorial2_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnFactorial2.Click
       Calculator1.varFact2 = CInt(txtValue.Text)
       btnFactorial2.Enabled = False
       ' Calculator1.FactorialMinusOne()
       Calculator1.ChooseThreads(2)
    End Sub
    
    Private Sub btnAddTwo_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnAddTwo.Click
       Calculator1.varAddTwo = CInt(txtValue.Text)
       btnAddTwo.Enabled = False
       ' Calculator1.AddTwo()
       Calculator1.ChooseThreads(3)
    End Sub
    
    Private Sub btnRunLoops_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnRunLoops.Click
       Calculator1.varLoopValue = CInt(txtValue.Text)
       btnRunLoops.Enabled = False
       ' Lets the user know that a loop is running.
       lblRunLoops.Text = "Looping"
       ' Calculator1.RunALoop()
       Calculator1.ChooseThreads(4)
    End Sub
    

Marshaling d'appels vers des contrôles

Vous allez à présent faciliter la mise à jour de l'affichage sur le formulaire. Les contrôles appartenant toujours au thread d'exécution principal, tout appel à un contrôle par un thread secondaire doit être un appel marshaling. Le marshaling consiste à déplacer un appel au-delà des limites d'un thread ; c'est une opération très coûteuse en termes de ressources. Pour réduire la quantité de marshaling nécessaire et garantir que la gestion de vos appels s'effectue en mode thread-safe, vous allez utiliser BeginInvoke pour appeler des méthodes sur le thread d'exécution principal. La quantité de marshaling requise à travers des frontières de threads est ainsi réduite. Ce type d'appel est nécessaire lors de l'appel à des méthodes qui manipulent des contrôles. Pour plus d'informations, consultez Comment : manipuler des contrôles à partir de threads.

Pour créer les procédures qui appellent les contrôles

  1. Ouvrez l'éditeur de code pour frmCalculations. Dans la section des déclarations, ajoutez le code suivant :

    Public Delegate Sub FHandler(ByVal Value As Double, ByVal _
       Calculations As Double)
    Public Delegate Sub A2Handler(ByVal Value As Integer, ByVal _
       Calculations As Double)
    Public Delegate Sub LDhandler(ByVal Calculations As Double, ByVal _
       Count As Integer)
    

    Invokeet BeginInvoke requièrent un délégué vers la méthode appropriée en tant qu'argument. Ces lignes déclarent les signatures du délégué qui seront utilisées par BeginInvoke pour appeler les méthodes appropriées.

  2. Ajoutez à votre code les méthodes vides suivantes :

    Public Sub FactHandler(ByVal Factorial As Double, ByVal TotalCalculations As _
       Double)
    End Sub
    Public Sub Fact1Handler(ByVal Factorial As Double, ByVal TotalCalculations As _
       Double)
    End Sub
    Public Sub Add2Handler(ByVal Result As Integer, ByVal TotalCalculations As _
       Double)
    End Sub
    Public Sub LDoneHandler(ByVal TotalCalculations As Double, ByVal Counter As _
       Integer)
    End Sub
    
  3. Dans le menu Edition, utilisez Couper et Coller pour couper tout le code de Sub Calculator1_FactorialComplete et le coller dans FactHandler.

  4. Répétez l'étape précédente pour Calculator1_FactorialMinusComplete et Fact1Handler, Calculator1_AddTwoComplete, Add2Handler, Calculator1_LoopComplete et LDoneHandler.

    Une fois ces opérations effectuées, il ne doit rester aucun code dans Calculator1_FactorialComplete, Calculator1_FactorialMinusComplete, Calculator1_AddTwoComplete et Calculator1_LoopComplete, et tout le code que ces méthodes utilisaient doit avoir été déplacé dans les nouvelles méthodes appropriées.

  5. Appelez la méthode BeginInvoke pour appeler les méthodes de façon asynchrone. Vous pouvez appeler BeginInvoke à partir de votre formulaire (me) ou d'un quelconque contrôle du formulaire.

    Private Sub Calculator1_FactorialComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialComplete
       ' BeginInvoke causes asynchronous execution to begin at the address
       ' specified by the delegate. Simply put, it transfers execution of 
       ' this method back to the main thread. Any parameters required by 
       ' the method contained at the delegate are wrapped in an object and 
       ' passed. 
       Me.BeginInvoke(New FHandler(AddressOf FactHandler), New Object() _
          {Factorial, TotalCalculations })
    End Sub
    
    Private Sub Calculator1_FactorialMinusComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialMinusComplete
       Me.BeginInvoke(New FHandler(AddressOf Fact1Handler), New Object() _
          { Factorial, TotalCalculations })
    End Sub
    
    Private Sub Calculator1_AddTwoComplete(ByVal Result As System.Int32, ByVal TotalCalculations As System.Double) Handles Calculator1.AddTwoComplete
       Me.BeginInvoke(New A2Handler(AddressOf Add2Handler), New Object() _
          { Result, TotalCalculations })
    End Sub
    
    Private Sub Calculator1_LoopComplete(ByVal TotalCalculations As System.Double, ByVal Counter As System.Int32) Handles Calculator1.LoopComplete
       Me.BeginInvoke(New LDHandler(AddressOf Ldonehandler), New Object() _
          { TotalCalculations, Counter })
    End Sub
    

    En apparence, le gestionnaire d'événements effectue un simple appel de la méthode suivante. En fait, il provoque l'appel d'une méthode sur le thread d'opération principal. Cette approche permet d'éviter les appels au-delà des limites des threads et permet l'exécution efficace des applications multithread, sans risque de blocage. Pour plus d'informations sur l'utilisation des contrôles dans un environnement multithread, consultez Comment : manipuler des contrôles à partir de threads.

  6. Enregistrez votre travail.

  7. Testez la solution en choisissant Démarrer le débogage dans le menu Déboguer.

    1. Tapez 10000000 dans la zone de texte, puis cliquez sur Run A Loop.

      "Looping" s'affiche dans l'étiquette située sous ce bouton. L'exécution de cette boucle doit être relativement longue. Si elle se termine trop tôt, ajustez la taille du nombre en conséquence.

    2. Cliquez rapidement sur les trois boutons encore activés. Vous constaterez qu'ils répondent tous. L'étiquette située sous Add Two doit être la première à afficher un résultat. Les résultats s'afficheront ensuite dans les étiquettes situées en dessous des boutons Factorial. Ces résultats sont infinis, dans la mesure où le nombre retourné par une factorielle 10 000 000 est trop grand pour tenir dans une variable à double précision. Enfin, après un délai supplémentaire, les résultats sont retournés sous le bouton Run A Loop.

      Comme vous venez de le voir, quatre ensembles de calculs distincts ont été effectués simultanément, sur quatre threads distincts. L'interface utilisateur a continué de répondre aux entrées et les résultats ont été retournés après l'exécution de chaque thread.

Coordination des threads

Un utilisateur expérimenté en matière d'applications multithread remarquera sans doute un subtil défaut dans le code saisi. Appelez de nouveau les lignes de code de chaque sous-routine de calcul dans Calculator :

varTotalCalculations += 1
varTotalAsOfNow = varTotalCalculations

Ces deux lignes de code incrémentent la variable publique varTotalCalculations et affectent cette valeur à la variable locale varTotalAsOfNow. Cette valeur est ensuite retournée à frmCalculations et affichée dans un contrôle de type étiquette. Cependant, la valeur retournée est-elle correcte ? Si un seul thread s'exécute, la réponse est clairement oui. En revanche, la réponse est plus incertaine dès lors que plusieurs threads s'exécutent. Chaque thread peut incrémenter la variable varTotalCalculations. Il est possible qu'après l'incrémentation de cette variable par un thread, un autre thread ait modifié cette variable en l'incrémentant avant que le premier thread n'ait copié la valeur dans varTotalAsOfNow. Cela signifie qu'il est possible que chaque thread renvoie en fait des résultats incorrects. Visual Basic propose l'instruction SyncLock, instruction qui permet de synchroniser les threads afin de garantir que chacun d'eux retourne toujours un résultat exact. La syntaxe de SyncLock est la suivante :

SyncLock AnObject
   Insert code that affects the object
   Insert some more
   Insert even more
' Release the lock
End SyncLock

Lors de l'entrée dans le bloc SyncLock, l'exécution sur l'expression spécifiée est bloquée aussi longtemps que le thread spécifié dispose d'un verrou exclusif sur l'objet en question. Dans l'exemple indiqué ci-dessus, l'exécution est bloquée sur AnObject. SyncLock doit être utilisé avec un objet qui retourne une référence plutôt qu'une valeur. L'exécution peut ensuite se poursuivre en tant que bloc, sans interférence avec les autres threads. Un ensemble d'instructions qui s'exécute en tant qu'unité est dit atomique. En présence de End SyncLock, l'expression est libérée et l'exécution des threads peut se poursuivre normalement.

Pour ajouter l'instruction SyncLock à votre application

  1. Ouvrez Calculator.vb dans l'éditeur de code.

  2. Localisez chaque instance du code suivant :

    varTotalCalculations += 1
    varTotalAsOfNow = varTotalCalculations
    

    Il doit y avoir quatre instances de ce code, une dans chaque méthode de calcul.

  3. Modifiez le code comme suit :

    SyncLock Me
       varTotalCalculations += 1
       varTotalAsOfNow = varTotalCalculations
    End SyncLock
    
  4. Enregistrez votre travail et testez-le comme dans l'exemple précédent.

    Il se peut que vous notiez une légère baisse des performances de votre programme. Cela est dû à l'interruption de l'exécution des threads en présence d'un verrou exclusif sur votre composant. Bien que cette solution garantisse l'exactitude des résultats, elle annule certains des avantages en matière de performances de la présence de plusieurs threads. Vous devez étudier avec soin la nécessité ou non de verrouiller les threads et n'implémenter cette méthode que lorsqu'elle s'avère absolument nécessaire.

Voir aussi

Tâches

Comment : coordonner plusieurs threads d'exécution

Procédure pas à pas : création d'un composant simple multithread à l'aide de Visual C#

Référence

BackgroundWorker

Concepts

Vue d'ensemble du modèle asynchrone basé sur des événements

Autres ressources

Programmation à l'aide de composants

Procédures pas à pas relatives à la programmation de composants

Multithreading dans les composants