Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
La lock instrucción adquiere el bloqueo de exclusión mutua para un objeto determinado, ejecuta un bloque de instrucciones y, a continuación, libera el bloqueo. Mientras se mantiene un bloqueo, el subproceso que lo mantiene puede volver a adquirir y liberar el bloqueo. Cualquier otro subproceso está bloqueado para adquirir el bloqueo y espera hasta que se libere el bloqueo. La lock instrucción garantiza que en el máximo solo un subproceso ejecute su cuerpo en cualquier momento en el tiempo.
La lock instrucción tiene la siguiente forma:
lock (x)
{
// Your code...
}
La variable x es una expresión de tipo o un tipo deSystem.Threading.Lock referencia. Cuando x se conoce en tiempo de compilación para que sea del tipo System.Threading.Lock, es exactamente equivalente a:
using (x.EnterScope())
{
// Your code...
}
El objeto devuelto por Lock.EnterScope() es un ref struct objeto que incluye un Dispose() método . La instrucción generada using garantiza que el ámbito se libere incluso si se produce una excepción con el cuerpo de la lock instrucción .
De lo contrario, la lock instrucción es exactamente equivalente a:
object __lockObj = x;
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
// Your code...
}
finally
{
if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}
Dado que el código usa una try-finally instrucción , el bloqueo se libera incluso si se produce una excepción dentro del cuerpo de una lock instrucción .
No se puede usar la await expresión en el cuerpo de una lock instrucción .
Directrices
A partir de .NET 9 y C# 13, bloquee una instancia de objeto dedicada del System.Threading.Lock tipo para obtener el mejor rendimiento. Además, el compilador emite una advertencia si un objeto conocido Lock se convierte en otro tipo y está bloqueado. Si usa una versión anterior de .NET y C#, bloquee una instancia de objeto dedicada que no se use para otro propósito. Evite usar la misma instancia de objeto de bloqueo para distintos recursos compartidos, ya que podría provocar interbloqueo o contención de bloqueo. En concreto, evite usar las siguientes instancias como objetos de bloqueo:
-
this, ya que los autores de llamadas también pueden bloquearthis. - Type instancias, ya que pueden obtenerse mediante el operador typeof o la reflexión.
- instancias de cadena, incluidos los literales de cadena, ya que se pueden internar.
Mantenga un bloqueo durante el menor tiempo posible para reducir la contención de bloqueos.
Ejemplo
En el ejemplo siguiente se define una Account clase que sincroniza el acceso a su campo privado balance bloqueando una instancia dedicada balanceLock . El uso de la misma instancia para el bloqueo garantiza que dos subprocesos diferentes no puedan actualizar el balance campo llamando a los Debit métodos o Credit simultáneamente. En el ejemplo se usa C# 13 y el nuevo Lock objeto . Si usa una versión anterior de C# o una biblioteca de .NET anterior, bloquee una instancia de object.
using System;
using System.Threading.Tasks;
public class Account
{
// Use `object` in versions earlier than C# 13
private readonly System.Threading.Lock _balanceLock = new();
private decimal _balance;
public Account(decimal initialBalance) => _balance = initialBalance;
public decimal Debit(decimal amount)
{
if (amount < 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "The debit amount cannot be negative.");
}
decimal appliedAmount = 0;
lock (_balanceLock)
{
if (_balance >= amount)
{
_balance -= amount;
appliedAmount = amount;
}
}
return appliedAmount;
}
public void Credit(decimal amount)
{
if (amount < 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "The credit amount cannot be negative.");
}
lock (_balanceLock)
{
_balance += amount;
}
}
public decimal GetBalance()
{
lock (_balanceLock)
{
return _balance;
}
}
}
class AccountTest
{
static async Task Main()
{
var account = new Account(1000);
var tasks = new Task[100];
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = Task.Run(() => Update(account));
}
await Task.WhenAll(tasks);
Console.WriteLine($"Account's balance is {account.GetBalance()}");
// Output:
// Account's balance is 2000
}
static void Update(Account account)
{
decimal[] amounts = [0, 2, -3, 6, -2, -1, 8, -5, 11, -6];
foreach (var amount in amounts)
{
if (amount >= 0)
{
account.Credit(amount);
}
else
{
account.Debit(Math.Abs(amount));
}
}
}
}
Especificación del lenguaje C#
Para obtener más información, vea la sección Instrucción lock de la especificación del lenguaje C#.