Lock — instrukcjaC# (odwołanie)lock statement (C# reference)

lock Instrukcja uzyskuje blokadę wykluczania wzajemnego dla danego obiektu, wykonuje blok instrukcji, a następnie zwalnia blokadę.The lock statement acquires the mutual-exclusion lock for a given object, executes a statement block, and then releases the lock. Gdy blokada jest utrzymywana, wątek, który przechowuje blokadę, może ponownie uzyskać i zwolnić blokadę.While a lock is held, the thread that holds the lock can again acquire and release the lock. Każdy inny wątek jest blokowany przed uzyskaniem blokady i czeka na zwolnienie blokady.Any other thread is blocked from acquiring the lock and waits until the lock is released.

lock Instrukcja ma postaćThe lock statement is of the form

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

gdzie x jest wyrażeniem typu odwołania.where x is an expression of a reference type. Jest to dokładnie równoważneIt's precisely equivalent to

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... blok finally jest uwalniany nawet wtedy, gdy wyjątek jest zgłaszany w treści lock instrukcji.Since the code uses a try...finally block, the lock is released even if an exception is thrown within the body of a lock statement.

Nie można użyć operatora await w treści lock instrukcji.You can't use the await operator in the body of a lock statement.

UwagiRemarks

Podczas synchronizowania dostępu do wątku z udostępnionym zasobem należy zablokować wystąpienie obiektu dedykowanego (na przykład private readonly object balanceLock = new object();) lub inne wystąpienie, które prawdopodobnie nie będzie używane jako obiekt blokady przez niepowiązane części kodu.When you synchronize thread access to a shared resource, lock on a dedicated object instance (for example, private readonly object balanceLock = new object();) or another instance that is unlikely to be used as a lock object by unrelated parts of the code. Należy unikać używania tego samego wystąpienia obiektu blokady dla różnych udostępnionych zasobów, ponieważ może to spowodować zakleszczenie lub zablokowanie rywalizacji.Avoid using the same lock object instance for different shared resources, as it might result in deadlock or lock contention. W szczególności należy unikać używania następujących funkcji jako obiektów blokowania:In particular, avoid using the following as lock objects:

  • this, ponieważ mogą one być używane przez wywołujących jako blokadę.this, as it might be used by the callers as a lock.
  • Typewystąpienia, ponieważ mogą być uzyskane przez operatora lub odbicie typeof .Type instances, as those might be obtained by the typeof operator or reflection.
  • wystąpienia ciągów, w tym literały ciągów, ponieważ mogą być internice.string instances, including string literals, as those might be interned.

PrzykładExample

W poniższym przykładzie zdefiniowano Account klasę, która synchronizuje dostęp do jego balance pola prywatnego przez zablokowanie balanceLock na dedykowanym wystąpieniu.The following example defines an Account class that synchronizes access to its private balance field by locking on a dedicated balanceLock instance. Użycie tego samego wystąpienia do blokowania gwarantuje, że balance nie można jednocześnie aktualizować pola przez dwa wątki próbujące Debit wywołać metody lub Credit .Using the same instance for locking ensures that the balance field cannot be updated simultaneously by two threads attempting to call the Debit or Credit methods simultaneously.

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)
    {
        lock (balanceLock)
        {
            if (balance >= amount)
            {
                Console.WriteLine($"Balance before debit :{balance, 5}");
                Console.WriteLine($"Amount to remove     :{amount, 5}");
                balance = balance - amount;
                Console.WriteLine($"Balance after debit  :{balance, 5}");
                return amount;
            }
            else
            {
                return 0;
            }
        }
    }

    public void Credit(decimal amount)
    {
        lock (balanceLock)
        {
            Console.WriteLine($"Balance before credit:{balance, 5}");
            Console.WriteLine($"Amount to add        :{amount, 5}");
            balance = balance + amount;
            Console.WriteLine($"Balance after credit :{balance, 5}");
        }
    }
}

class AccountTest
{
    static void Main()
    {
        var account = new Account(1000);
        var tasks = new Task[100];
        for (int i = 0; i < tasks.Length; i++)
        {
            tasks[i] = Task.Run(() => RandomlyUpdate(account));
        }
        Task.WaitAll(tasks);
    }

    static void RandomlyUpdate(Account account)
    {
        var rnd = new Random();
        for (int i = 0; i < 10; i++)
        {
            var amount = rnd.Next(1, 100);
            bool doCredit = rnd.NextDouble() < 0.5;
            if (doCredit)
            {
                account.Credit(amount);
            }
            else
            {
                account.Debit(amount);
            }
        }
    }
}

specyfikacja języka C#C# language specification

Aby uzyskać więcej informacji, zobacz sekcję blokowanie instrukcji C# specyfikacji języka.For more information, see The lock statement section of the C# language specification.

Zobacz takżeSee also