SyncLock, instruction

Acquiert un verrou exclusif pour un bloc d’instructions avant l’exécution du bloc.

Syntaxe

SyncLock lockobject  
    [ block ]  
End SyncLock  

Éléments

lockobject
Obligatoire. Expression qui prend la valeur d’une référence d’objet.

block
Optionnel. Bloc d’instructions à exécuter quand le verrou est acquis.

End SyncLock
met fin à un bloc SyncLock.

Notes

L’instruction SyncLock garantit que le bloc d’instructions n’est pas exécuté par plusieurs threads à la fois. SyncLock empêche chaque thread d’entrer dans le bloc tant que celui-ci est exécuté par un autre thread.

SyncLock est le plus souvent utilisé pour empêcher la mise à jour de données par plusieurs threads à la fois. Si les instructions qui manipulent les données doivent s’exécuter jusqu’à leur achèvement sans interruption, placez-les dans un bloc SyncLock.

Un bloc d’instructions protégé par un verrou exclusif est parfois appelé section critique.

Règles

  • Création de branches. Vous ne pouvez pas créer de branche dans un bloc SyncLock à partir de l’extérieur du bloc.

  • Valeur de l’objet de verrouillage. La valeur de lockobject ne peut pas être Nothing. Vous devez créer l’objet de verrouillage avant de l’utiliser dans une instruction SyncLock.

    Vous ne pouvez pas modifier la valeur de lockobject pendant l’exécution d’un bloc SyncLock. Le mécanisme nécessite que l’objet de verrouillage reste inchangé.

  • Vous ne pouvez pas utiliser l’opérateur Await dans un bloc SyncLock.

Comportement

  • Mécanisme. Quand un thread atteint l’instruction SyncLock, il évalue l’expression lockobject et suspend l’exécution jusqu’à ce qu’il acquière un verrou exclusif sur l’objet retourné par l’expression. Quand un autre thread atteint l’instruction SyncLock, il n’acquiert pas de verrou tant que le premier thread exécute l’instruction End SyncLock.

  • Données protégées. Si lockobject est une variable Shared, le verrou exclusif empêche tout thread (dans n’importe quelle instance de la classe) d’exécuter le bloc SyncLock tant qu’il est exécuté par un autre thread. Cela protège les données partagées entre toutes les instances.

    Si lockobject est une variable d’instance (non Shared), le verrou empêche tout thread en cours d’exécution dans l’instance actuelle d’exécuter le bloc SyncLock en même temps qu’un autre thread dans la même instance. Cela protège les données gérées par l’instance individuelle.

  • Acquisition et libération. Un bloc SyncLock se comporte comme une construction Try...Finally dans laquelle le bloc Try acquiert un verrou exclusif sur lockobject et le bloc Finally le libère. Ainsi, le bloc SyncLock garantit la libération du verrou, quelle que soit la façon dont vous quittez le bloc. C’est vrai même dans le cas d’une exception non prise en charge.

  • Appels de framework. Le bloc SyncLock acquiert et libère le verrou exclusif en appelant les méthodes Enter et Exit de la classe Monitor dans l’espace de noms System.Threading.

Pratiques de programmation

L’expression lockobject doit toujours prendre la valeur d’un objet qui appartient exclusivement à votre classe. Vous devez déclarer une variable objet Private pour protéger les données appartenant à l’instance actuelle ou une variable objet Private Shared pour protéger les données communes à toutes les instances.

Vous ne devez pas utiliser le mot clé Me pour fournir un objet de verrouillage pour des données d’instance. Si du code externe à votre classe contient une référence à une instance de votre classe, il peut utiliser cette référence comme objet de verrouillage pour un bloc SyncLock complètement différent du vôtre, protégeant différentes données. Ainsi, votre classe et l’autre classe peuvent s’empêcher mutuellement d’exécuter leurs blocs SyncLock non liés. De même, le verrouillage sur une chaîne peut être problématique, car tout autre code dans le processus utilisant la même chaîne partagera le même verrou.

Vous ne devez pas non plus utiliser la méthode Me.GetType pour fournir un objet de verrouillage pour des données partagées. En effet, GetType retourne toujours le même objet Type pour un nom de classe donné. Le code externe peut appeler GetType sur votre classe et obtenir le même objet de verrouillage que celui que vous utilisez. Ainsi, les deux classes s’empêcheront mutuellement d’exécuter leurs blocs SyncLock.

Exemples

Description

L’exemple suivant montre une classe qui gère une simple liste de messages. Elle stocke les messages dans un tableau et le dernier élément utilisé de ce tableau dans une variable. La procédure addAnotherMessage incrémente le dernier élément et stocke le nouveau message. Ces deux opérations sont protégées par les instructions SyncLock et End SyncLock, car après l’incrémentation du dernier élément, le nouveau message doit être stocké avant qu’un autre thread puisse à nouveau l’incrémenter.

Si la classe simpleMessageList partageait une liste de messages entre toutes ses instances, les variables messagesList et messagesLast seraient déclarées comme Shared. Dans ce cas, la variable messagesLock devrait également être Shared pour qu’un seul objet de verrouillage soit utilisé par chaque instance.

Code

Class simpleMessageList
    Public messagesList() As String = New String(50) {}
    Public messagesLast As Integer = -1
    Private messagesLock As New Object
    Public Sub addAnotherMessage(ByVal newMessage As String)
        SyncLock messagesLock
            messagesLast += 1
            If messagesLast < messagesList.Length Then
                messagesList(messagesLast) = newMessage
            End If
        End SyncLock
    End Sub
End Class

Description

L’exemple suivant utilise des threads et SyncLock. Tant que l’instruction SyncLock est présente, le bloc d’instructions est une section critique et balance ne devient jamais un nombre négatif. Vous pouvez commenter les instructions SyncLock et End SyncLock pour voir ce qui se passe quand vous omettez le mot clé SyncLock.

Code

Imports System.Threading

Module Module1

    Class Account
        Dim thisLock As New Object
        Dim balance As Integer

        Dim r As New Random()

        Public Sub New(ByVal initial As Integer)
            balance = initial
        End Sub

        Public Function Withdraw(ByVal amount As Integer) As Integer
            ' This condition will never be true unless the SyncLock statement
            ' is commented out:
            If balance < 0 Then
                Throw New Exception("Negative Balance")
            End If

            ' Comment out the SyncLock and End SyncLock lines to see
            ' the effect of leaving out the SyncLock keyword.
            SyncLock thisLock
                If balance >= amount Then
                    Console.WriteLine("Balance before Withdrawal :  " & balance)
                    Console.WriteLine("Amount to Withdraw        : -" & amount)
                    balance = balance - amount
                    Console.WriteLine("Balance after Withdrawal  :  " & balance)
                    Return amount
                Else
                    ' Transaction rejected.
                    Return 0
                End If
            End SyncLock
        End Function

        Public Sub DoTransactions()
            For i As Integer = 0 To 99
                Withdraw(r.Next(1, 100))
            Next
        End Sub
    End Class

    Sub Main()
        Dim threads(10) As Thread
        Dim acc As New Account(1000)

        For i As Integer = 0 To 9
            Dim t As New Thread(New ThreadStart(AddressOf acc.DoTransactions))
            threads(i) = t
        Next

        For i As Integer = 0 To 9
            threads(i).Start()
        Next
    End Sub

End Module

Commentaires

Voir aussi