instruction lock (référence C#)lock statement (C# reference)

L’instruction lock obtient le verrou d’exclusion mutuelle d’un objet donné, exécute un bloc d’instructions, puis libère le verrou.The lock statement acquires the mutual-exclusion lock for a given object, executes a statement block, and then releases the lock. Tant qu’un verrou est maintenu, le thread qui contient le verrou peut à nouveau obtenir et libérer le verrou.While a lock is held, the thread that holds the lock can again acquire and release the lock. Tout autre thread se voit bloquer l’obtention du verrou et attend que ce dernier soit libéré.Any other thread is blocked from acquiring the lock and waits until the lock is released.

L’instruction lock se présente sous la formeThe lock statement is of the form

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

x est une expression de type référence.where x is an expression of a reference type. Elle équivaut précisément àIt'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);
}

Dans la mesure où le code utilise un bloc try...finally, le verrou est libéré même si une exception est levée dans le corps d’une instruction 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.

Vous ne pouvez pas utiliser l’opérateur await dans le corps d’une instruction lock.You can't use the await operator in the body of a lock statement.

NotesRemarks

Quand vous synchronisez l’accès des threads à une ressource partagée, verrouillez une instance d’objet dédiée (par exemple private readonly object balanceLock = new object();) ou toute autre instance peu susceptible d’être utilisée comme objet de verrouillage par des parties du code non associées.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. Évitez d’utiliser la même instance d’objet de verrouillage pour différentes ressources partagées, car cela peut entraîner une contention d’interblocage ou de verrouillage.Avoid using the same lock object instance for different shared resources, as it might result in deadlock or lock contention. En particulier, évitez d’utiliser les éléments suivants en tant qu’objets de verrouillage :In particular, avoid using the following as lock objects:

  • this, qui peut être utilisé en tant que verrou par les appelants.this, as it might be used by the callers as a lock.
  • Les instances de Type, qui peuvent être obtenues par l’opérateur typeof ou par réflexion.Type instances, as those might be obtained by the typeof operator or reflection.
  • Les instances de chaîne, notamment les littéraux de chaîne, qui peuvent être internés.string instances, including string literals, as those might be interned.

ExempleExample

L’exemple suivant définit une classe Account, qui synchronise l’accès à son champ balance privé en verrouillant une instance balanceLock dédiée.The following example defines an Account class that synchronizes access to its private balance field by locking on a dedicated balanceLock instance. L’utilisation de la même instance pour le verrouillage permet de garantir que le champ balance ne peut pas être mis à jour simultanément par deux threads qui tentent d’appeler les méthodes Debit ou Credit en même temps.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);
            }
        }
    }
}

spécification du langage C#C# language specification

Pour plus d’informations, voir la section Instruction lock de la spécification du langage C#.For more information, see The lock statement section of the C# language specification.

Voir aussiSee also