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
je ve formuláři.
lock (x)
{
// Your code...
}
where x
je výraz typu odkazu. Je to přesně ekvivalentem
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
Při synchronizaci přístupu vlákna ke sdílenému prostředku zamkněte instanci vyhrazeného objektu (například private readonly object balanceLock = new object();
) nebo jinou instanci, která není pravděpodobně použita jako objekt zámku nesouvisejícími částmi kódu. 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í ho můžou používat jako zámek.- 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 balance
pole nelze aktualizovat současně dvěma vlákny, které se pokoušejí volat Debit
nebo Credit
metody současně.
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));
}
}
}
}
specifikace jazyka C#
Další informace naleznete v části Příkaz lock specifikace jazyka C#.
Viz také
Váš názor
https://aka.ms/ContentUserFeedback.
Připravujeme: V průběhu roku 2024 budeme postupně vyřazovat problémy z GitHub coby mechanismus zpětné vazby pro obsah a nahrazovat ho novým systémem zpětné vazby. Další informace naleznete v tématu:Odeslat a zobrazit názory pro