lock 语句(C# 参考)lock statement (C# reference)

lock 语句获取给定对象的互斥 lock,执行语句块,然后释放 lock。The lock statement acquires the mutual-exclusion lock for a given object, executes a statement block, and then releases the lock. 持有 lock 时,持有 lock 的线程可以再次获取并释放 lock。While a lock is held, the thread that holds the lock can again acquire and release the lock. 阻止任何其他线程获取 lock 并等待释放 lock。Any other thread is blocked from acquiring the lock and waits until the lock is released.

lock 语句具有以下格式The lock statement is of the form

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

其中 x引用类型的表达式。where x is an expression of a reference type. 它完全等同于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);
}

由于该代码使用 try...finally 块,即使在 lock 语句的正文中引发异常,也会释放 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.

lock 语句的正文中不能使用 await 关键字。You can't use the await keyword in the body of a lock statement.

备注Remarks

当同步对共享资源的线程访问时,请锁定专用对象实例(例如,private readonly object balanceLock = new object();)或另一个不太可能被代码无关部分用作 lock 对象的实例。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. 避免对不同的共享资源使用相同的 lock 对象实例,因为这可能导致死锁或锁争用。Avoid using the same lock object instance for different shared resources, as it might result in deadlock or lock contention. 具体而言,避免将以下对象用作 lock 对象:In particular, avoid using the following as lock objects:

  • this(调用方可能将其用作 lock)。this, as it might be used by the callers as a lock.
  • Type 实例(可以通过 typeof 运算符或反射获取)。Type instances, as those might be obtained by the typeof operator or reflection.
  • 字符串实例,包括字符串文本,(这些可能是暂存的)。string instances, including string literals, as those might be interned.

示例Example

以下示例定义了一个 Account 类,该类通过锁定专用的 balanceLock 实例来同步对其专用 balance 字段的访问。The following example defines an Account class that synchronizes access to its private balance field by locking on a dedicated balanceLock instance. 使用相同的实例进行锁定可确保尝试同时调用 DebitCredit 方法的两个线程无法同时更新 balance 字段。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);
            }
        }
    }
}

C# 语言规范C# language specification

有关详细信息,请参阅 C# 语言规范中的 lock 语句部分。For more information, see The lock statement section of the C# language specification.

请参阅See also