SyncLock (Instrucción)

Adquiere un bloqueo exclusivo para un bloque de instrucciones antes de ejecutar el bloque.

Sintaxis

SyncLock lockobject  
    [ block ]  
End SyncLock  

Partes

lockobject
Necesario. Expresión que se evalúa como una referencia de objeto.

block
Opcional. Bloque de instrucciones que se van a ejecutar cuando se adquiere el bloqueo.

End SyncLock
Finaliza un bloque SyncLock.

Comentarios

La instrucción SyncLock garantiza que varios subprocesos no ejecuten el bloque de instrucciones al mismo tiempo. SyncLock evita que cada subproceso entre en el bloque hasta que no haya otro subproceso ejecutándolo.

El uso más común de SyncLock es proteger los datos de ser actualizados por más de un subproceso de manera simultánea. Si las instrucciones que manipulan los datos se deben completar sin interrupción, colóquelas dentro de un bloque SyncLock.

A veces, un bloque de instrucciones protegido por un bloqueo exclusivo se denomina sección crítica.

Reglas

  • Ramificación. No se pueden crear ramas en un bloque SyncLock desde fuera del bloque.

  • Valor del objeto de bloqueo. El valor de lockobject no puede ser Nothing. Debe crear el objeto de bloqueo antes de usarlo en una instrucción SyncLock.

    No se puede cambiar el valor de lockobject al ejecutar un bloque SyncLock. El mecanismo requiere que el objeto de bloqueo permanezca sin cambios.

  • No se puede usar el operador Await en un bloque SyncLock.

Comportamiento

  • Mecanismo. Cuando un subproceso alcanza la instrucción SyncLock, evalúa la expresión lockobject y suspende la ejecución hasta que adquiere un bloqueo exclusivo en el objeto devuelto por la expresión. Cuando otro subproceso alcanza la instrucción SyncLock, no adquiere un bloqueo hasta que el primer subproceso ejecuta la instrucción End SyncLock.

  • Datos protegidos. Si lockobject es una variable Shared, el bloque exclusivo impide que un subproceso en cualquier instancia de la clase ejecute el bloque SyncLock mientras haya otro subproceso ejecutándolo. Esto protege los datos que se comparten entre todas las instancias.

    Si lockobject es una variable de instancia (no Shared), el bloqueo impide que un subproceso que se ejecuta en la instancia actual ejecute el bloque SyncLock al mismo tiempo que otro subproceso en la misma instancia. Esto protege los datos que mantiene la instancia individual.

  • Adquisición y liberación. Un bloque SyncLock se comporta como una construcción Try...Finally en la que el bloque Try adquiere un bloqueo exclusivo en lockobject y el bloque Finally lo libera. Por este motivo, el bloque SyncLock garantiza la liberación del bloqueo, independientemente de cómo salga del bloque. Esto es cierto incluso en el caso de una excepción no controlada.

  • Llamadas del marco. El bloque SyncLock adquiere y libera el bloqueo exclusivo mediante una llamada a los métodos Enter y Exit de la clase Monitor en el espacio de nombres System.Threading.

Procedimientos de programación

La expresión lockobject siempre se debe evaluar como un objeto que pertenece exclusivamente a la clase. Debe declarar una variable de objeto Private para proteger los datos que pertenecen a la instancia actual o una variable de objeto Private Shared para proteger los datos comunes a todas las instancias.

No debe usar la palabra clave Me para proporcionar un objeto de bloqueo para los datos de instancia. Si el código externo a la clase tiene una referencia a una instancia de la clase, podría usar dicha referencia como un objeto de bloqueo para un bloque SyncLock completamente distinto del suyo, protegiendo así datos diferentes. De este modo, su clase y la otra clase podrían bloquearse entre para no ejecutar sus bloques SyncLock no relacionados. De manera similar, el bloqueo en una cadena puede ser problemático, ya que cualquier otro código del proceso que use la misma cadena compartirá el mismo bloqueo.

Tampoco debe usar el método Me.GetType para proporcionar un objeto de bloqueo para los datos compartidos. Esto se debe a que GetType siempre devuelve el mismo objeto Type para un nombre de clase determinado. El código externo podría llamar a GetType en su clase y obtener el mismo objeto de bloqueo que está usando. Esto daría lugar a que las dos clases se bloqueen entre sí desde sus bloques SyncLock.

Ejemplos

Descripción

En el ejemplo siguiente,- se muestra una clase que mantiene una lista de mensajes sencilla. Contiene los mensajes de una matriz y el último elemento usado de dicha matriz en una variable. El procedimiento addAnotherMessage incrementa el último elemento y almacena el mensaje nuevo. Estas dos operaciones están protegidas por las instrucciones SyncLock y End SyncLock, porque una vez que se incrementó el último elemento, el mensaje nuevo se debe almacenar antes de que cualquier otro subproceso pueda incrementar nuevamente el último elemento.

Si la clase simpleMessageList compartió una lista de mensajes entre todas sus instancias, las variables messagesList y messagesLast se declararían como Shared. En este caso, la variable messagesLock también debe ser Shared para que haya un objeto de bloqueo único utilizado por cada instancia.

Código

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

Descripción

En el ejemplo siguiente, se usan subprocesos y SyncLock. Siempre que la instrucciónSyncLock esté presente, el bloque de instrucciones es una sección crítica y balance nunca se convertirá en un número negativo. Puede convertir las instrucciones SyncLock y End SyncLock en comentarios para ver el efecto de omitir la palabra clave SyncLock.

Código

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

Comentarios

Vea también