Condividi tramite


Istruzione SyncLock

Acquisisce un blocco esclusivo per un blocco di istruzioni prima di eseguire quest'ultimo.

SyncLock lockobject
    [ block ]
End SyncLock

Parti

  • lockobject
    Obbligatoria. Espressione che restituisce un riferimento a un oggetto.

  • block
    Facoltativo. Blocco di istruzioni da eseguire quando viene acquisito il blocco.

  • End SyncLock
    Consente di terminare un blocco SyncLock.

Note

L'istruzione SyncLock garantisce che più thread non eseguano contemporaneamente il blocco di istruzioni. SyncLock impedisce a ciascun thread di accedere al blocco durante l'esecuzione di quest'ultimo da parte di altri thread.

SyncLock viene in genere utilizzato per evitare che i dati vengano aggiornati da più thread contemporaneamente. Se le istruzioni che modificano i dati devono essere completate senza interruzioni, è opportuno inserirle in un blocco SyncLock.

Un blocco di istruzioni protetto da un blocco esclusivo viene talvolta definito sezione critica.

Regole

  • Diramazione. Non è consentita la diramazione in un blocco SyncLock dall'esterno del blocco.

  • Valore dell'oggetto di blocco. Il valore di lockobject non può essere Nothing. Per utilizzare l'oggetto di blocco in un'istruzione SyncLock, è necessario prima crearlo.

    Non è possibile modificare il valore di lockobject durante l'esecuzione di un blocco SyncLock. Per il corretto funzionamento del meccanismo è necessario che l'oggetto di blocco rimanga invariato.

Comportamento

  • Meccanismo. Quando un thread raggiunge l'istruzione SyncLock, valuta l'espressione lockobject e sospende l'esecuzione fino all'acquisizione di un blocco esclusivo sull'oggetto restituito dall'espressione. Quando un altro thread raggiunge l'istruzione SyncLock, non acquisisce un blocco fino a quando il primo thread non esegue l'istruzione End SyncLock.

  • Dati protetti. Se lockobject è una variabile Shared, il blocco esclusivo impedisce a un thread di qualsiasi istanza della classe di eseguire il blocco di istruzioni SyncLock durante l'esecuzione di quest'ultimo da parte di altri thread. In questo modo, i dati condivisi tra tutte le istanze risultano protetti.

    Se lockobject è una variabile di istanza (non Shared), il blocco impedisce a un thread in esecuzione nell'istanza corrente di eseguire il blocco di istruzioni SyncLock contestualmente a un altro thread della stessa istanza. In questo modo, i dati gestiti dalla singola istanza risultano protetti.

  • Acquisizione e rilascio. Un blocco di istruzioni SyncLock si comporta come una costruzione Try...Finally in cui il blocco di istruzioni Try acquisisce un blocco esclusivo su lockobject e il blocco di istruzioni Finally lo rilascia. Per questo motivo, SyncLock garantisce il rilascio del blocco indipendentemente dalle modalità con cui viene terminato il blocco di istruzioni. Ciò vale anche per le eccezioni non gestite.

  • Chiamate Framework. Il blocco di istruzioni SyncLock acquisisce e rilascia il blocco esclusivo effettuando chiamate ai metodi Enter ed Exit della classe Monitor nello spazio dei nomi System.Threading.

Tecniche di programmazione

L'espressione lockobject dovrebbe sempre restituire un oggetto appartenente esclusivamente alla classe utilizzata. Per la protezione di dati appartenenti all'istanza corrente è necessario dichiarare una variabile oggetto Private, mentre per la protezione di dati comuni a tutte le istanze è necessario dichiarare una variabile oggetto Private Shared.

Si consiglia di non utilizzare la parola chiave Me per fornire un oggetto di blocco per i dati di istanza. Se nel codice esterno alla classe utilizzata è presente un riferimento a un'istanza di tale classe, è possibile utilizzare il riferimento come oggetto di blocco per un blocco di istruzioni SyncLock completamente diverso, in modo da proteggere dati differenti. In questo modo, la classe utilizzata e l'altra classe possono bloccarsi reciprocamente impedendo l'esecuzione dei rispettivi blocchi di istruzioni SyncLock non correlati. Analogamente, il blocco su una stringa può generare problemi poiché qualsiasi altro codice nel processo che utilizza la stessa stringa condividerà lo stesso blocco.

Si consiglia inoltre di non utilizzare il metodo Me.GetType per fornire un oggetto di blocco per i dati condivisi. GetType, infatti, restituisce sempre lo stesso oggetto Type per un determinato nome di classe. Il codice esterno può chiamare GetType sulla classe e ottenere lo stesso oggetto di blocco che si sta utilizzando. In questo modo, le due classi si bloccano reciprocamente impedendo l'esecuzione dei rispettivi blocchi di istruzioni SyncLock.

Esempi

Oggetto di descrizione

Nell'esempio riportato di seguito viene illustrata una classe che gestisce un semplice elenco di messaggi. I messaggi sono inclusi in una matrice e l'ultimo elemento utilizzato della matrice è incluso in una variabile. La routine addAnotherMessage incrementa l'ultimo elemento e memorizza il nuovo messaggio. Queste due operazioni sono protette dalle istruzioni SyncLock e End SyncLock. Dopo che l'ultimo elemento è stato incrementato, infatti, il nuovo messaggio deve essere memorizzato prima che un altro thread possa incrementare di nuovo l'ultimo elemento.

Se la classe simpleMessageList condivide un unico elenco di messaggi tra tutte le istanze, le variabili messagesList e messagesLast verranno dichiarate come Shared. In questo caso, anche la variabile messagesLock deve essere Shared, in modo che ogni istanza possa utilizzare un unico oggetto di blocco.

Codice

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

Oggetto di descrizione

Nell'esempio riportato di seguito vengono utilizzati dei thread e SyncLock. Finché l'istruzione SyncLock è presente, il blocco di istruzioni costituisce una sezione critica e balance non risulterà mai un numero negativo. È possibile commentare SyncLock e le istruzioni End SyncLock per vedere l'effetto di nascondere la parola chiave SyncLock.

Codice

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

Vedere anche

Riferimenti

System.Threading

Monitor

Sincronizzazione di thread (C# e Visual Basic)

Concetti

Threading (C# e Visual Basic)