目录
1. 锁机制 (Locking Mechanisms)
(1) lock 关键字
(2) Monitor 类
2. 跨进程互斥机制
3. 信号量机制
(1) Semaphore 和 SemaphoreSlim
4. 读写锁机制
(1) ReaderWriterLockSlim
5. 原子操作机制
(1) Interlocked 类
6. 自旋锁机制
(1) SpinLock
线程互斥是一种保证在同一时刻只有一个线程能够访问共享资源的机制。它是一种排他性的访问控制,目的是防止多个线程同时对共享资源进行读写操作而导致的数据不一致或其他错误。比如多个线程同时对一个银行账户进行取款操作,如果不进行互斥控制,就可能出现账户余额错误的情况。
在 .NET 中,线程互斥(Mutual Exclusion)有多种实现方式,以下是 .NET 中常见的线程互斥实现方式:
实现方式 | 特点 | 使用场景 |
lock关键字 | 简单易用,自动管理锁 | 进程内同步 |
Monitor类 | 更细粒度控制锁 | 显式控制锁的场景 |
Mutex类 | 支持跨进程 | 多进程间同步 |
信号量Semaphore | 限制最大并发数 | 控制资源访问数量 |
读写锁 ReaderWriterLockSlim | 允许多线程同时读,但只允许一个线程写 | 读多写少的场景 |
Interlocked类 | 原子操作,高效 | 简单数值操作 |
自旋锁SpinLock | 自旋等待锁,避免上下文切换 | 短时间临界区 |
根据具体需求选择合适的线程互斥机制,能够有效提升程序的性能和可靠性。
1. 锁机制 (Locking Mechanisms)
(1) lock 关键字
- 原理: lock 是 C# 中的一个语法糖,它实际上是调用了 Monitor.Enter 和 Monitor.Exit 方法来实现的。当一个线程执行到 lock 语句块时,它会尝试获取锁对象上的排他锁。如果成功获取,则进入临界区执行代码;否则,该线程将被阻塞直到锁可用。
- 特点:
- 简单易用,适用于单一应用程序域内的线程同步。
- 自动处理异常情况下的锁释放。
- 示例:
private readonly object _lockObject = new object();public void CriticalSection()
{lock (_lockObject){// 访问共享资源的代码}
}
(2) Monitor 类
- 原理: Monitor 提供了比 lock 更灵活的同步机制,允许手动控制进入和退出临界区。通过调用 Monitor.Enter 获取锁,Monitor.Exit 释放锁。此外,还支持条件变量(Wait, Pulse, PulseAll)以实现更复杂的同步逻辑。
- 特点:
- 支持超时等待获取锁的能力。
- 可以与条件变量一起使用,支持复杂的同步模式。
- 示例:
private readonly object _lockObject = new object();public void CriticalSection()
{try{Monitor.Enter(_lockObject);// 访问共享资源的代码}finally{Monitor.Exit(_lockObject);}
}
2. 跨进程互斥机制
(1) Mutex 类
- 原理: Mutex 是一种内核级别的同步对象,可以用于跨进程的线程同步。当一个线程获取了 Mutex 对象的所有权时,其他线程会被阻塞,直到该线程释放 Mutex。
- 特点:
- 支持跨进程同步。
- 性能较 lock 和 Monitor 稍低,因为它是内核对象。
- 示例:
using System.Threading;private static Mutex _mutex = new Mutex();public void CriticalSection()
{_mutex.WaitOne(); // 获取锁try{// 访问共享资源的代码}finally{_mutex.ReleaseMutex(); // 释放锁}
}
3. 信号量机制
(1) Semaphore 和 SemaphoreSlim
- 原理: 信号量是一种允许多个线程同时访问共享资源的同步机制,但限制最大并发数。Semaphore 支持跨进程,而 SemaphoreSlim 是轻量级版本,仅限于当前进程内使用。
- 特点:
- Semaphore 支持跨进程同步。
- SemaphoreSlim 性能更高,适合单进程场景。
示例: (SemaphoreSlim)
using System.Threading;private static SemaphoreSlim _semaphore = new SemaphoreSlim(1); // 最大并发数为 1public async Task CriticalSectionAsync()
{await _semaphore.WaitAsync();try{// 访问共享资源的代码}finally{_semaphore.Release();}
}
4. 读写锁机制
(1) ReaderWriterLockSlim
- 原理: ReaderWriterLockSlim 是一种优化的读写锁,允许多个线程同时读取共享资源,但只允许一个线程写入资源。通过 EnterReadLock、EnterWriteLock 和 EnterUpgradeableReadLock 方法分别管理读锁、写锁和可升级的读锁。
- 特点:
- 适合读多写少的场景。
- 提高了读操作的并发性能。
- 示例:
private static ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();public void ReadOperation()
{_rwLock.EnterReadLock();try{// 读取共享资源的代码}finally{_rwLock.ExitReadLock();}
}public void WriteOperation()
{_rwLock.EnterWriteLock();try{// 写入共享资源的代码}finally{_rwLock.ExitWriteLock();}
}
5. 原子操作机制
(1) Interlocked 类
- 原理: Interlocked 提供了一些原子操作方法,用于对简单数据类型(如整数)进行线程安全的操作。这些操作在硬件级别上保证了原子性,无需显式加锁。
- 特点:
- 高效,无需显式加锁。
- 适用于简单的数值操作。
- 示例:
using System.Diagnostics.Metrics;private int _counter = 0;public void IncrementCounter()
{Interlocked.Increment(ref _counter);
}public void DecrementCounter()
{Interlocked.Decrement(ref _counter);
}
6. 自旋锁机制
(1) SpinLock
- 原理: SpinLock 是一种自旋锁,线程会不断循环检查锁是否可用,而不是进入等待状态。这种方式避免了上下文切换开销,但在长时间锁定时会浪费 CPU 资源。
- 特点:
- 适合非常短的临界区。
- 避免上下文切换的开销。
- 示例:
using System.Threading;private SpinLock _spinLock = new SpinLock();public void CriticalSection()
{bool lockTaken = false;try{_spinLock.Enter(ref lockTaken);// 访问共享资源的代码}finally{if (lockTaken)_spinLock.Exit();}
}