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 serNothing
. Debe crear el objeto de bloqueo antes de usarlo en una instrucciónSyncLock
.No se puede cambiar el valor de
lockobject
al ejecutar un bloqueSyncLock
. 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ónlockobject
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ónSyncLock
, no adquiere un bloqueo hasta que el primer subproceso ejecuta la instrucciónEnd SyncLock
.Datos protegidos. Si
lockobject
es una variableShared
, el bloque exclusivo impide que un subproceso en cualquier instancia de la clase ejecute el bloqueSyncLock
mientras haya otro subproceso ejecutándolo. Esto protege los datos que se comparten entre todas las instancias.Si
lockobject
es una variable de instancia (noShared
), el bloqueo impide que un subproceso que se ejecuta en la instancia actual ejecute el bloqueSyncLock
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ónTry...Finally
en la que el bloqueTry
adquiere un bloqueo exclusivo enlockobject
y el bloqueFinally
lo libera. Por este motivo, el bloqueSyncLock
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étodosEnter
yExit
de la claseMonitor
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