Příkaz lock – zajištění výhradního přístupu ke sdílenému prostředku
Příkaz lock
získá zámek vzájemného vyloučení pro daný objekt, spustí blok příkazu a pak uvolní zámek. Zatímco se zámek drží, vlákno, které zámek obsahuje, může zámek znovu získat a uvolnit. Jakékoli jiné vlákno se zablokuje v získání zámku a počká, až se zámek uvolní. Tento lock
příkaz zajistí, že v každém okamžiku spustí tělo maximálně jednoho vlákna.
Příkaz lock
má následující formu:
lock (x)
{
// Your code...
}
Proměnná x
je výraz typu System.Threading.Lock nebo odkazový typ. Pokud x
je známo, že v době kompilace má být typ System.Threading.Lock, je přesně ekvivalentní:
using (x.EnterScope())
{
// Your code...
}
Objekt vrácený Lock.EnterScope() je objekt ref struct
, který zahrnuje metodu Dispose()
. Vygenerovaný using
příkaz zajistí uvolnění oboru i v případě, že je vyvolán výjimka s tělem lock
příkazu.
lock
V opačném případě je příkaz přesně ekvivalentní:
object __lockObj = x;
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
// Your code...
}
finally
{
if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}
Vzhledem k tomu, že kód používá try-finally
příkaz, zámek se uvolní i v případě, že je v těle lock
příkazu vyvolán výjimka.
Výraz nelze použít await
v textu lock
příkazu.
Pokyny
Počínaje rozhraním .NET 9 a C# 13 zamkněte vyhrazenou instanci objektu System.Threading.Lock typu pro zajištění nejlepšího výkonu. Kromě toho kompilátor vydá upozornění, pokud je známý Lock
objekt přetypován na jiný typ a uzamčen. Pokud používáte starší verzi .NET a C#, zamkněte vyhrazenou instanci objektu, která se nepoužívá k jinému účelu. Nepoužívejte stejnou instanci objektu uzamčení pro různé sdílené prostředky, protože může vést k zablokování nebo kolizí uzamčení. Zejména nepoužívejte následující instance jako objekty zámků:
this
, protože volající mohou také uzamknoutthis
.- Type instance, protože mohou být získány typeof operátor nebo reflexe.
- instance řetězců, včetně řetězcových literálů, protože se můžou prolínat.
Držte zámek co nejkratší dobu, abyste snížili kolize zámků.
Příklad
Následující příklad definuje Account
třídu, která synchronizuje přístup k jeho privátnímu balance
poli uzamčením vyhrazené balanceLock
instance. Použití stejné instance pro uzamčení zajišťuje, že dvě různá vlákna nemohou aktualizovat balance
pole voláním Debit
nebo Credit
metod současně. Ukázka používá C# 13 a nový Lock
objekt. Pokud používáte starší verzi jazyka C# nebo starší knihovnu .NET, zamkněte instanci 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));
}
}
}
}
specifikace jazyka C#
Další informace naleznete v části Příkaz lock specifikace jazyka C#.