lock – příkaz (Referenční příručka jazyka C#)

Příkaz získá zámek vzájemného vyloučení pro daný objekt, spustí blok příkazu lock a pak zámek uvolní. Zatímco je zámek podrženou, vlákno, které zámek drží, může zámek znovu získat a uvolnit. Jakékoli jiné vlákno má zablokované získání zámku a počká, dokud se zámek neuvolní.

Příkaz lock má tvar .

lock (x)
{
    // Your code...
}

where x je výraz typu odkazu. Je 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á metodu try... blok finally, zámek je uvolněn i v případě, že je v těle příkazu vyvolána lock výjimka.

V těle příkazu nelze použít operátor lock await.

Pokyny

Když synchronizujete přístup z více vláken ke sdílenému prostředku, uzamkujte vyhrazenou instanci objektu (například ) nebo jinou instanci, která se pravděpodobně nebude používat jako objekt zámku nesouvisejícími částmi private readonly object balanceLock = new object(); kódu. Nepoužívejte stejnou instanci objektu zámku pro různé sdílené prostředky, protože by mohlo dojít k vzájemnému zablokování nebo zamknout nápor. Nepoužívejte zejména následující objekty jako objekty uzamčení:

  • this, protože ho můžou použít volající jako zámek.
  • Type instance, protože je lze získat pomocí operátoru typeof nebo reflexe.
  • instance řetězců, včetně řetězcových literálů, protože ty mohou být stážisty.

Přidržte zámek co nejkratší dobu, abyste snížili počet náporů zámků.

Příklad

Následující příklad definuje třídu, která synchronizuje přístup ke svému Account privátnímu balance poli uzamčením ve vyhrazené balanceLock instanci. Použití stejné instance k zamykání zajistí, že pole nelze aktualizovat současně dvěma vlákny, která se pokoušejí volat metody balance Debit nebo Credit 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 najdete v části Příkaz lock specifikace jazyka C#.

Viz také