instrução lock (referência em C#)lock statement (C# reference)

A instrução lock obtém o bloqueio de exclusão mútua para um determinado objeto, executa um bloco de instruções e, em seguida, libera o bloqueio.The lock statement acquires the mutual-exclusion lock for a given object, executes a statement block, and then releases the lock. Embora um bloqueio seja mantido, o thread que mantém o bloqueio pode adquiri-lo novamente e liberá-lo.While a lock is held, the thread that holds the lock can again acquire and release the lock. Qualquer outro thread é impedido de adquirir o bloqueio e aguarda até que ele seja liberado.Any other thread is blocked from acquiring the lock and waits until the lock is released.

A instrução lock está no formatoThe lock statement is of the form

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

em que x é uma expressão de um tipo de referência.where x is an expression of a reference type. Ela é precisamente 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);
}

Como o código usa um bloco try...finally, o bloqueio será liberado mesmo se uma exceção for gerada dentro do corpo de uma instrução 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.

Não é possível usar o operador await no corpo de uma instrução lock.You can't use the await operator in the body of a lock statement.

ComentáriosRemarks

Ao sincronizar o acesso de thread com um recurso compartilhado, bloqueie uma instância de objeto dedicada (por exemplo, private readonly object balanceLock = new object();) ou outra instância que provavelmente não será usada como um objeto de bloqueio por partes não relacionadas do 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 usar a mesma instância de objeto de bloqueio para diferentes recursos compartilhados, uma vez que ela poderia resultar em deadlock ou contenção de bloqueio.Avoid using the same lock object instance for different shared resources, as it might result in deadlock or lock contention. Especificamente, evite usar os seguintes itens como objetos de bloqueio:In particular, avoid using the following as lock objects:

  • this, uma vez que pode ser usado pelos chamadores como um bloqueio.this, as it might be used by the callers as a lock.
  • Instâncias Type, pois podem ser obtidas pelo operador ou reflexão typeof.Type instances, as those might be obtained by the typeof operator or reflection.
  • Instâncias de cadeia de caracteres, incluindo literais de cadeia de caracteres, pois podem ser internalizadas.string instances, including string literals, as those might be interned.

ExemploExample

O exemplo a seguir define uma classe Account que sincroniza o acesso com seu campo privado balance bloqueando uma instância balanceLock dedicada.The following example defines an Account class that synchronizes access to its private balance field by locking on a dedicated balanceLock instance. Usar a mesma instância para bloquear garante que o campo balance não pode ser atualizado simultaneamente por dois threads que tentam chamar os métodos Debit ou Credit simultaneamente.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);
            }
        }
    }
}

Especificação da linguagem C#C# language specification

Para saber mais, confira a seção A instrução lock na especificação da linguagem C#.For more information, see The lock statement section of the C# language specification.

Consulte tambémSee also