一、核心作用
lock用于同步多线程对共享资源的访问,确保同一时间只有一个线程执行被锁定的代码块。其语法为:
lock (object lockObj) {
// 临界区代码
}
- 作用对象:需传入一个对象实例(如private readonly object _lock = new object();)作为锁标识。
- 适用场景:适用于需要独占访问的共享资源(如集合、计数器等)。
二、实现原理
lock基于Monitor类实现,底层通过Monitor.Enter和Monitor.Exit方法控制:
1. 加锁:调用Monitor.Enter尝试获取锁,若失败则阻塞当前线程。
2. 解锁:代码块执行完毕后,Monitor.Exit释放锁,唤醒等待线程。
三、关键注意事项
1. 避免锁定公共对象
不要使用this、类型对象(如typeof(MyClass))或可被外部访问的对象作为锁,可能导致意外死锁。
2. 锁定范围最小化
尽量缩短临界区代码长度,减少线程阻塞时间,避免性能瓶颈。
3. 异常处理
即使发生异常,lock块仍会自动释放锁(通过try-finally机制),无需手动处理。
4. 死锁风险
避免在多个锁之间形成循环依赖(如A线程锁住Obj1后等待Obj2,而B线程锁住Obj2后等待Obj1)。
四、替代方案
根据场景选择更高效的同步机制:
1. ReaderWriterLockSlim
适用于读多写少的场景,允许多个读线程同时访问。
2. ConcurrentQueue等线程安全集合
直接使用.NET内置的线程安全集合类,避免手动加锁。
3. Interlocked类
用于原子操作(如递增计数器),性能更高。
五、总结建议
- 优先使用:简单场景下lock足够且易用。
- 复杂场景:根据读写比例、资源类型选择专用同步工具。
- 性能优化:通过减少锁竞争(如分段锁)或异步编程降低阻塞影响。