Istruzione lock (Riferimenti per C#)lock statement (C# reference)

L'istruzione lock acquisisce il blocco a esclusione reciproca per un oggetto specificato, esegue un blocco di istruzioni e quindi rilascia il blocco.The lock statement acquires the mutual-exclusion lock for a given object, executes a statement block, and then releases the lock. Mentre è attivo un blocco, il thread che contiene il blocco può ancora acquisire e rilasciare il blocco.While a lock is held, the thread that holds the lock can again acquire and release the lock. Gli altri thread non possono acquisire il blocco e devono attendere finché il blocco non viene rilasciato.Any other thread is blocked from acquiring the lock and waits until the lock is released.

L'istruzione lock ha il formatoThe lock statement is of the form

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

in cui x è un'espressione di un tipo riferimento.where x is an expression of a reference type. È esattamente equivalente aIt'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);
}

Poiché il codice usa un blocco try... finally, il blocco viene rilasciato anche se viene generata un'eccezione nel corpo di un'istruzione lock.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.

Non è possibile usare l'operatore await nel corpo di un'istruzione lock.You can't use the await operator in the body of a lock statement.

OsservazioniRemarks

Quando si sincronizza l'accesso dei thread a una risorsa condivisa, applicare il blocco a un'istanza dell'oggetto dedicata (ad esempio private readonly object balanceLock = new object();) o a un'altra istanza che ha poche probabilità di essere usata come oggetto di blocco da parti del codice non correlate.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. Evitare di usare la stessa istanza di oggetto di blocco per diverse risorse condivise. Questo può originare problemi di deadlock o conflitti di blocco.Avoid using the same lock object instance for different shared resources, as it might result in deadlock or lock contention. In particolare, evitare di usare gli elementi seguenti come oggetti di blocco:In particular, avoid using the following as lock objects:

  • this, perché potrebbe essere usato dai chiamanti come blocco.this, as it might be used by the callers as a lock.
  • Istanze Type, in quanto possono essere ottenute dall'operatore typeof o dalla reflection.Type instances, as those might be obtained by the typeof operator or reflection.
  • Istanze stringa, tra cui i valori letterali stringa, in quanto potrebbero essere centralizzate.string instances, including string literals, as those might be interned.

EsempioExample

L'esempio seguente definisce una classe Account che sincronizza l'accesso al proprio campo balance privato applicando il blocco su un'istanza balanceLock dedicata.The following example defines an Account class that synchronizes access to its private balance field by locking on a dedicated balanceLock instance. L'uso della stessa istanza per il blocco garantisce che il campo balance non possa essere aggiornato contemporaneamente da due thread che provano a chiamare i metodi Debit o Credit allo stesso tempo.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);
            }
        }
    }
}

Specifiche del linguaggio C#C# language specification

Per altre informazioni, vedere la sezione Istruzione lock della specifica del linguaggio C#.For more information, see The lock statement section of the C# language specification.

Vedere ancheSee also