lock (Instrucción, Referencia de C#)lock statement (C# Reference)

La instrucción lock adquiere el bloqueo de exclusión mutua de un objeto determinado, ejecuta un bloque de instrucciones y luego libera el bloqueo.The lock statement acquires the mutual-exclusion lock for a given object, executes a statement block, and then releases the lock. Mientras se mantiene un bloqueo, el subproceso que lo mantiene puede volver a adquirir y liberar el bloqueo.While a lock is held, the thread that holds the lock can again acquire and release the lock. Ningún otro subproceso puede adquirir el bloqueo y espera hasta que se libera.Any other thread is blocked from acquiring the lock and waits until the lock is released.

La instrucción lock tiene el formatoThe lock statement is of the form

lock (x)
{
    // Your code...
}

donde x es una expresión de un tipo de referencia.where x is an expression of a reference type. Es exactamente equivalente aIt's precisely equivalent to

object __lockObj = x;
bool __lockWasTaken = false;
try
{
    System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
    // Your code...
}
finally
{
    if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}

Puesto que el código usa un bloque try... finally, el bloqueo se libera aunque se produzca una excepción dentro del cuerpo de una instrucción lock.Since the code uses a try...finally block, the lock is released even if an exception is thrown within the body of a lock statement.

No se puede usar la palabra clave await en el cuerpo de una instrucción lock.You can't use the await keyword in the body of a lock statement.

ComentariosRemarks

Al sincronizar el acceso del subproceso al recurso compartido, bloquee una instancia dedicada de objeto (por ejemplo, private readonly object balanceLock = new object();) u otra instancia cuyo empleo como objeto de bloqueo sea poco probable por parte de elementos no relacionados del código.When you synchronize thread access to a shared resource, lock on a dedicated object instance (for example, private readonly object balanceLock = new object();) or another instance that is unlikely to be used as a lock object by unrelated parts of the code. Evite el uso de la misma instancia de objeto de bloqueo para distintos recursos compartidos, ya que se podría producir un interbloqueo o una contención de bloqueo.Avoid using the same lock object instance for different shared resources, as it might result in deadlock or lock contention. En particular, evite utilizar lo siguiente como objetos de bloqueo:In particular, avoid using the following as lock objects:

  • this, porque los autores de llamadas podrían usarlo como un bloqueo.this, as it might be used by the callers as a lock.
  • Instancias Type, porque el operador o la reflexión typeof podrían obtenerlas.Type instances, as those might be obtained by the typeof operator or reflection.
  • Instancias de cadena, incluidos literales de cadena, porque podrían internarse.string instances, including string literals, as those might be interned.

EjemploExample

En el ejemplo siguiente se define una clase Account que sincroniza el acceso a su campo privado balance mediante el bloqueo de una instancia dedicada balanceLock.The following example defines an Account class that synchronizes access to its private balance field by locking on a dedicated balanceLock instance. El empleo de la misma instancia para bloquear garantiza que el campo balance no sea actualizado al mismo tiempo por dos subprocesos que intentan llamar a los métodos Debit o Credit simultáneamente.Using the same instance for locking ensures that the balance field cannot be updated simultaneously by two threads attempting to call the Debit or Credit methods simultaneously.

using System;
using System.Threading.Tasks;

public class Account
{
    private readonly object balanceLock = new object();
    private decimal balance;

    public Account(decimal initialBalance)
    {
        balance = initialBalance;
    }

    public decimal Debit(decimal amount)
    {
        lock (balanceLock)
        {
            if (balance >= amount)
            {
                Console.WriteLine($"Balance before debit :{balance, 5}");
                Console.WriteLine($"Amount to remove     :{amount, 5}");
                balance = balance - amount;
                Console.WriteLine($"Balance after debit  :{balance, 5}");
                return amount;
            }
            else
            {
                return 0;
            }
        }
    }

    public void Credit(decimal amount)
    {
        lock (balanceLock)
        {
            Console.WriteLine($"Balance before credit:{balance, 5}");
            Console.WriteLine($"Amount to add        :{amount, 5}");
            balance = balance + amount;
            Console.WriteLine($"Balance after credit :{balance, 5}");
        }
    }
}

class AccountTest
{
    static void Main()
    {
        var account = new Account(1000);
        var tasks = new Task[100];
        for (int i = 0; i < tasks.Length; i++)
        {
            tasks[i] = Task.Run(() => RandomlyUpdate(account));
        }
        Task.WaitAll(tasks);
    }

    static void RandomlyUpdate(Account account)
    {
        var rnd = new Random();
        for (int i = 0; i < 10; i++)
        {
            var amount = rnd.Next(1, 100);
            bool doCredit = rnd.NextDouble() < 0.5;
            if (doCredit)
            {
                account.Credit(amount);
            }
            else
            {
                account.Debit(amount);
            }
        }
    }
}

Especificación del lenguaje C#C# language specification

Para obtener más información, consulte la Especificación del lenguaje C#.For more information, see the C# Language Specification. La especificación del lenguaje es la fuente definitiva de la sintaxis y el uso de C#.The language specification is the definitive source for C# syntax and usage.

Vea tambiénSee also