C# 中的 lock 关键字是用于同步代码块,确保在同一时间内只有一个线程可以执行该代码块。这是解决多线程环境下资源竞争和数据不一致问题的常用手段之一。
lock 关键字通过锁定一个对象来工作,任何线程在尝试进入被 lock 保护的代码块之前,都必须先获得该对象的锁。如果一个线程已经获得了锁,其他线程就必须等待,直到锁被释放。当线程退出 lock 代码块时,无论是因为正常执行完毕还是由于异常退出,锁都会被自动释放。
使用 lock 的基本语法
object lockObject = new object(); // 用于锁定的对象,通常是一个私有的、只读的字段 public void Method()
{ lock (lockObject) { // 访问或修改被保护的资源 // ... }
}
注意事项
- 锁对象的选择:锁对象必须是引用类型,通常是一个私有的、只读的字段,以避免外部代码意外地锁定或解锁对象。
- 避免死锁:在复杂的多线程程序中,如果不当使用 lock,可能会导致死锁(两个或多个线程相互等待对方释放锁)。设计时要特别注意锁的获取顺序。
- 性能考虑:虽然 lock 是解决多线程同步问题的有效手段,但它也会引入性能开销。频繁地锁定和解锁可能会降低程序的性能。
- 异常处理:即使 lock 代码块中发生了异常,锁也会自动释放,但你应该确保异常处理逻辑能够正确处理 lock 代码块中可能发生的任何问题。
- 避免锁定公共类型或 string:因为公共类型或字符串字面量可能被程序中的多个部分共享,这可能导致不期望的锁定行为。
- 使用 Monitor 类:lock 关键字实际上是对 System.Threading.Monitor 类的封装。如果你需要更细粒度的控制(如尝试获取锁而不阻塞线程),你可以直接使用 Monitor 类。
示例
private readonly object _lockObject = new object(); public void IncrementCount()
{ lock (_lockObject) { // 模拟对共享资源的访问 _count++; // 假设 _count 是一个需要线程安全的字段 }
}
在这个例子中,_lockObject 是一个私有字段,用于锁定 IncrementCount 方法中的代码块,以确保在任何给定时间只有一个线程可以执行该代码块中的代码。