Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
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 uzyskać i zwolnić blokadę wiele razy. Każdy inny wątek nie może nabyć blokady i czeka na zwolnienie blokady. Instrukcja lock zapewnia, że co najwyżej jeden wątek wykonuje treść w dowolnym momencie.
Dokumentacja języka C# zawiera ostatnio wydaną wersję języka C#. Zawiera również początkową dokumentację funkcji w publicznej wersji zapoznawczej nadchodzącej wersji językowej.
Dokumentacja identyfikuje dowolną funkcję po raz pierwszy wprowadzoną w ostatnich trzech wersjach języka lub w bieżącej publicznej wersji zapoznawczej.
Wskazówka
Aby dowiedzieć się, kiedy funkcja została po raz pierwszy wprowadzona w języku C#, zapoznaj się z artykułem dotyczącym historii wersji języka C#.
Instrukcja lock ma następującą formę:
lock (x)
{
// Your code...
}
Zmienna x jest wyrażeniem System.Threading.Lock typu lub typem odwołania. Gdy kompilator wie, że x jest to typ System.Threading.Lock, jest dokładnie odpowiednikiem:
using (x.EnterScope())
{
// Your code...
}
Obiekt zwracany przez Lock.EnterScope() element jest obiektem ref struct zawierającym metodę Dispose() . Instrukcja generated using gwarantuje, że zakres zostanie zwolniony, nawet jeśli wyjątek zostanie zgłoszony w treści instrukcji lock .
lock W przeciwnym razie instrukcja jest dokładnie równoważna:
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
Począwszy od platform .NET 9 i C# 13, zablokuj dedykowane wystąpienie System.Threading.Lock obiektu typu, aby uzyskać najlepszą wydajność. Kompilator wyświetla również ostrzeżenie, jeśli rzutujesz znany Lock obiekt na inny typ i zablokujesz go. Jeśli używasz starszej wersji platformy .NET i języka C#, zablokuj wystąpienie dedykowanego obiektu, które nie jest używane w innym celu. 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ż osoby wywołujące mogą również zablokowaćthisfunkcję . - 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 dwa różne wątki nie mogą zaktualizować balance pola przez wywołanie Debit metod lub Credit jednocześnie. W przykładzie użyto języka C# 13 i nowego Lock obiektu. Jeśli używasz starszej wersji języka C# lub starszej biblioteki .NET, zablokuj wystąpienie programu 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));
}
}
}
}
Specyfikacja języka C#
Aby uzyskać więcej informacji, zobacz sekcję instrukcji lockspecyfikacji języka C#.