C# lock (this) 如何理解

493 阅读3分钟
  1. lock关键字的基本作用

    • 在 C# 中,lock关键字用于实现线程同步,它确保在同一时刻只有一个线程能够访问被锁定的代码块。这在多线程编程环境中非常重要,因为多个线程同时访问和修改共享资源时,可能会导致数据不一致、竞态条件等问题。
  2. lock (this)的具体含义

    • 锁定对象lock关键字后面的表达式(在这里是this)指定了一个对象作为锁对象。this是一个引用,它指向当前实例对象。当一个线程执行到lock (this)语句时,它会尝试获取当前对象的锁。如果锁没有被其他线程占用,那么该线程就可以获得锁并进入被锁定的代码块;如果锁已经被其他线程占用,那么这个线程就会被阻塞,直到锁被释放。
    • 示例场景:假设你有一个银行账户类BankAccount,它有一个方法用于从账户余额中取款。在多线程环境下,如果多个线程同时调用这个取款方法,就可能会出现问题。代码可能如下:
class BankAccount 
{ 
    private decimal balance; 
    public BankAccount(decimal initialBalance)
    { 
       balance = initialBalance; 
    } 
    public void Withdraw(decimal amount) 
    { 
         lock (this) 
         { 
             if (balance >= amount) 
             { 
              balance -= amount; Console.WriteLine($"成功取款{amount},余额为{balance}"); 
             } 
             else 
             { 
              Console.WriteLine("余额不足"); 
             }
         } 
     } 
 }

在这个例子中,lock (this)用于保护Withdraw方法中的余额检查和更新操作。当一个线程进入Withdraw方法并获取了this(也就是当前BankAccount对象)的锁后,其他线程就不能同时访问这个方法,直到第一个线程完成取款操作并释放锁。这样就避免了多个线程同时修改balance导致的数据不一致问题。

  1. lock (this)的潜在问题

    • 外部可见性问题lock (this)可能会导致外部代码能够访问到这个锁对象,从而可能会产生一些意想不到的问题。例如,外部代码可能会在不适当的时候锁定这个对象,导致死锁情况的发生。假设你有两个类AB,它们都有lock (this)的代码块,并且在某些情况下,A的方法在获取了自己的锁后试图访问B,而B的方法也在获取自己的锁后试图访问A,这样就会造成死锁。

    • 继承问题:如果一个类使用lock (this),并且这个类被继承,那么子类的实例在作为锁对象时可能会出现问题。因为子类可能会有自己的同步需求,而lock (this)是基于当前实例(包括父类部分)的,可能无法满足子类的特殊同步要求。

在很多情况下,更推荐使用一个私有对象作为锁对象(例如private readonly object lockObject = new object();,然后lock (lockObject)),这样可以避免lock (this)带来的潜在问题。