Declaração de bloqueio - Garantir acesso exclusivo a um recurso compartilhado
A lock
instrução adquire o bloqueio de exclusão mútua para um determinado objeto, executa um bloco de instrução e, em seguida, libera o bloqueio. Enquanto uma fechadura é segurada, a rosca que segura a fechadura pode novamente adquirir e liberar a fechadura. Qualquer outro thread é impedido de adquirir o bloqueio e aguarda até que o bloqueio seja liberado. A lock
instrução garante que, no máximo, apenas um thread execute seu corpo a qualquer momento.
A lock
declaração é da forma
lock (x)
{
// Your code...
}
onde x
é uma expressão de um tipo de referência. É precisamente 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);
}
Como o código usa uma try-finally
instrução, o bloqueio é liberado mesmo se uma exceção for lançada dentro do corpo de uma lock
instrução.
Não é possível usar a await
expressão no corpo de uma lock
instrução.
Diretrizes
Quando você sincroniza o acesso de thread a 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. Evite usar a mesma instância de objeto de bloqueio para diferentes recursos compartilhados, pois isso pode resultar em impasse ou contenção de bloqueio. Em particular, evite usar as seguintes instâncias como objetos de bloqueio:
this
, pois pode ser usado pelos chamadores como um bloqueio.- Typeinstâncias, como podem ser obtidas pelo tipo de operador ou reflexão.
- ocorrências de cadeia de caracteres, incluindo literais de cadeia de caracteres, como eles podem ser internados.
Segure um cadeado pelo menor tempo possível para reduzir a contenção do bloqueio.
Exemplo
O exemplo a seguir define uma Account
classe que sincroniza o acesso ao seu campo privado balance
bloqueando uma instância dedicada balanceLock
. Usar a mesma instância para bloqueio garante que o balance
campo não possa ser atualizado simultaneamente por dois threads que tentam chamar os Debit
métodos ou Credit
simultaneamente.
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)
{
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));
}
}
}
}
Especificação da linguagem C#
Para obter mais informações, consulte a seção A instrução de bloqueio da especificação da linguagem C#.
Consulte também
Comentários
https://aka.ms/ContentUserFeedback.
Brevemente: Ao longo de 2024, vamos descontinuar progressivamente o GitHub Issues como mecanismo de feedback para conteúdos e substituí-lo por um novo sistema de feedback. Para obter mais informações, veja:Submeter e ver comentários