lock — zapewnianie wyłącznego dostępu do zasobu udostępnionego
Instrukcja lock
uzyskuje blokadę wzajemnego wykluczania dla danego obiektu, wykonuje blok instrukcji, a następnie zwalnia blokadę. Gdy blokada jest utrzymywana, wątek, który przechowuje blokadę, może ponownie uzyskać i zwolnić blokadę. Każdy inny wątek nie może nabyć blokady i czeka na zwolnienie blokady. Instrukcja lock
zapewnia, że w dowolnym momencie tylko jeden wątek wykonuje treść.
Instrukcja lock
ma postać
lock (x)
{
// Your code...
}
gdzie x
jest wyrażeniem typu odwołania. Jest to dokładnie równoważne
object __lockObj = x;
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
// Your code...
}
finally
{
if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}
Ponieważ kod używa try-finally
instrukcji, blokada jest zwalniana, nawet jeśli wyjątek jest zgłaszany w treści instrukcji lock
.
Nie można użyć await
wyrażenia w treści instrukcji lock
.
Wytyczne
Podczas synchronizowania dostępu wątku do zasobu udostępnionego zablokuj wystąpienie dedykowanego obiektu (na przykład private readonly object balanceLock = new object();
) lub inne wystąpienie, które jest mało prawdopodobne, aby było używane jako obiekt blokady przez niepowiązane części kodu. Unikaj używania tego samego wystąpienia obiektu blokady dla różnych zasobów udostępnionych, ponieważ może to spowodować zakleszczenie lub zablokowanie rywalizacji. W szczególności należy unikać używania następujących wystąpień jako obiektów blokady:
this
, ponieważ może być używany przez osoby wywołujące jako blokadę.- Type wystąpienia, ponieważ mogą być uzyskiwane przez operator typeof lub odbicie.
- wystąpienia ciągów, w tym literały ciągów, ponieważ mogą być internowane.
Przytrzymaj blokadę tak szybko, jak to możliwe, aby zmniejszyć rywalizację o blokadę.
Przykład
W poniższym przykładzie zdefiniowano klasę Account
, która synchronizuje dostęp z polem prywatnym balance
przez zablokowanie wystąpienia dedykowanego balanceLock
. Użycie tego samego wystąpienia do blokowania gwarantuje, że balance
pole nie może być aktualizowane jednocześnie przez dwa wątki próbujące wywołać Debit
metody lub Credit
jednocześnie.
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));
}
}
}
}
specyfikacja języka C#
Aby uzyskać więcej informacji, zobacz sekcję instrukcji lock specyfikacji języka C#.
Zobacz też
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla