欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > C#中的锁机制详解

C#中的锁机制详解

2025/6/13 9:33:41 来源:https://blog.csdn.net/yuanpan/article/details/148547970  浏览:    关键词:C#中的锁机制详解

在C#中,锁是用于多线程编程中同步访问共享资源的重要机制。以下是C#中主要的锁类型及其特点和应用场景:

1. lock 关键字 (Monitor类)

​特点​​:

  • 最常用的锁机制,实际上是语法糖,底层使用Monitor
  • 提供互斥访问,同一时间只允许一个线程进入临界区
  • 支持重入(同一线程可以多次获取同一个锁)
  • 不支持超时设置
  • 基于对象引用作为同步对象

​应用场景​​:

  • 简单的线程同步需求
  • 保护共享数据结构的访问
  • 需要简单互斥的场景

​示例​​:

csharp

复制

private readonly object _lockObj = new object();void ThreadSafeMethod()
{lock (_lockObj){// 临界区代码}
}

2. Monitor 类

​特点​​:

  • lock关键字的底层实现
  • 提供更多控制,如TryEnter方法可设置超时
  • 支持条件等待(Wait/Pulse/PulseAll
  • 需要手动释放锁

​应用场景​​:

  • 需要超时控制的同步场景
  • 需要更精细控制的线程同步
  • 生产者-消费者模式

​示例​​:

csharp

复制

private readonly object _monitorObj = new object();void ThreadSafeMethod()
{if (Monitor.TryEnter(_monitorObj, TimeSpan.FromSeconds(1))){try{// 临界区代码}finally{Monitor.Exit(_monitorObj);}}
}

3. Mutex (互斥体)

​特点​​:

  • 系统范围的锁,可用于跨进程同步
  • Monitor更重量级,性能开销更大
  • 支持命名,可用于跨进程同步
  • 支持超时设置
  • 需要手动释放

​应用场景​​:

  • 跨进程的同步需求
  • 需要系统范围互斥的场景
  • 长时间持有的锁

​示例​​:

csharp

复制

using var mutex = new Mutex(false, "Global\\MyNamedMutex");try
{if (mutex.WaitOne(TimeSpan.FromSeconds(1))){// 临界区代码}
}
finally
{mutex.ReleaseMutex();
}

4. Semaphore 和 SemaphoreSlim

​特点​​:

  • 信号量,允许多个线程同时访问资源(数量可控制)
  • Semaphore是系统范围的,SemaphoreSlim是轻量级的进程内版本
  • 适合资源池场景
  • SemaphoreSlim性能更好,但不支持跨进程

​应用场景​​:

  • 限制并发访问数量
  • 资源池管理
  • 需要允许多个线程同时访问但有限制的场景

​示例​​:

csharp

复制

private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(3, 3); // 允许3个并发async Task AccessResourceAsync()
{await _semaphore.WaitAsync();try{// 临界区代码(最多3个线程同时执行)}finally{_semaphore.Release();}
}

5. ReaderWriterLock 和 ReaderWriterLockSlim

​特点​​:

  • 读写锁,允许多个读或单个写
  • ReaderWriterLockSlim是改进版本,性能更好
  • 支持递归(可配置)
  • 读锁可升级为写锁

​应用场景​​:

  • 读多写少的场景
  • 需要区分读写访问的共享资源
  • 数据库缓存等高频读取偶尔写入的场景

​示例​​:

csharp

复制

private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();void ReadData()
{_rwLock.EnterReadLock();try{// 读取操作}finally{_rwLock.ExitReadLock();}
}void WriteData()
{_rwLock.EnterWriteLock();try{// 写入操作}finally{_rwLock.ExitWriteLock();}
}

6. SpinLock

​特点​​:

  • 自旋锁,线程会忙等待而不是阻塞
  • 轻量级,适合短时间等待
  • 不支持递归(默认)
  • 值类型,没有GC压力

​应用场景​​:

  • 极短时间的临界区保护
  • 高频率、低竞争的同步场景
  • 性能关键的代码路径

​示例​​:

csharp

复制

private SpinLock _spinLock = new SpinLock();void ThreadSafeMethod()
{bool lockTaken = false;try{_spinLock.Enter(ref lockTaken);// 临界区代码}finally{if (lockTaken)_spinLock.Exit();}
}

7. Interlocked 类

​特点​​:

  • 提供原子操作,无需锁
  • 性能极高
  • 仅限于简单操作(递增、递减、比较交换等)

​应用场景​​:

  • 简单的原子操作
  • 计数器等简单共享变量
  • 无锁编程

​示例​​:

csharp

复制

private int _counter = 0;void IncrementCounter()
{Interlocked.Increment(ref _counter);
}

选择锁的指导原则

  1. ​简单优先​​:优先使用最简单的lock关键字,除非有特殊需求
  2. ​范围考虑​​:进程内同步优先选择轻量级锁,跨进程才用Mutex或命名Semaphore
  3. ​读写比例​​:读多写少考虑ReaderWriterLockSlim
  4. ​等待时间​​:短时间等待考虑SpinLock,长时间等待用MonitorMutex
  5. ​并发控制​​:需要限制并发数用SemaphoreSlim
  6. ​原子操作​​:简单操作优先考虑Interlocked

避免常见问题

  1. ​死锁​​:避免嵌套锁,按固定顺序获取多个锁
  2. ​锁粒度​​:锁的粒度要适中,过大影响并发,过小增加开销
  3. ​锁持续时间​​:尽量减少持有锁的时间
  4. ​递归锁​​:谨慎使用递归锁,可能导致意外行为
  5. ​异常处理​​:确保在finally块中释放锁

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词