lock、Interlocked、Monitor、SpinLock、WaitHandle、Mutex、Semaphore、Events、Barrier、ReaderWriterLockSlim 这些同步机制虽然都用于多线程同步,但它们的底层实现、使用场景和性能特点各不相同。
1. lock 关键字
底层操作:
-
lock是基于Monitor实现的语法糖。 -
底层调用
Monitor.Enter和Monitor.Exit方法。 -
使用
lock时,编译器会自动生成try-finally块,确保锁的释放。
特点:
-
基于内核对象的同步机制(
Monitor内部使用SyncBlock)。 -
适用于简单的临界区保护。
-
可能会引入死锁问题。
适用场景:
-
简单的线程同步,保护共享资源。用于简单的线程同步,确保同一时间只有一个线程访问共享资源。
private static readonly object _lock = new object();private static int _counter = 0;public static void IncrementCounter(){lock (_lock){_counter++;}}
2. Interlocked 类
底层操作:
-
使用 CPU 的原子指令(如
LOCK CMPXCHG)实现原子操作。 -
直接操作内存,无需锁。
特点:
-
轻量级,性能高。
-
仅支持简单的原子操作(如
Increment、Decrement、Exchange、CompareExchange)。 -
不适用于复杂的同步逻辑。
适用场景:
-
对简单类型(如
int、long)进行原子操作。用于对简单类型的原子操作,如递增、递减、交换等。
private static int _counter = 0;public static void IncrementCounter(){Interlocked.Increment(ref _counter);}
3. Monitor 类
底层操作:
-
基于
SyncBlock实现,SyncBlock是 CLR 为每个对象分配的一个内部数据结构。 -
使用
Monitor.Enter和Monitor.Exit方法获取和释放锁。 -
支持
Wait、Pulse和PulseAll方法,用于线程间的信号通知。
特点:
-
比
lock更灵活,支持复杂的同步逻辑。 -
可能会引入死锁问题。
-
性能较低,因为涉及内核对象的切换。
适用场景:
-
需要复杂同步逻辑的场景(如条件等待)。与
lock类似,但提供了更多的控制,如TryEnter、Wait、Pulse等。
private static readonly object _lock = new object();public static void DoWork(){Monitor.Enter(_lock);try{// 临界区代码}finally{Monitor.Exit(_lock);}}
4. SpinLock 结构
底层操作:
-
使用自旋(
spin)机制,线程在获取锁失败时会不断重试,而不是立即进入等待状态。 -
基于 CPU 的原子操作实现。
特点:
-
适用于短时间的等待,避免线程上下文切换的开销。
-
长时间等待会浪费 CPU 资源。
-
轻量级,性能高。
适用场景:
-
短时间的临界区保护,锁竞争不激烈的场景。适用于短时间的等待,避免线程上下文切换的开销。
private static SpinLock _spinLock = new SpinLock();public static void DoWork(){bool lockTaken = false;try{_spinLock.Enter(ref lockTaken);// 临界区代码}finally{if (lockTaken)_spinLock.Exit();}}
5. WaitHandle 类
底层操作:
-
基于内核对象(如事件、信号量、互斥体)实现。
-
使用
WaitOne、WaitAll和WaitAny方法等待信号。
特点:
-
支持跨进程同步。
-
性能较低,因为涉及内核模式的切换。
-
适用于复杂的线程同步场景。
适用场景:
-
线程间的信号通知,复杂的同步逻辑。用于线程间的信号通知,如等待某个事件发生。
private static EventWaitHandle _waitHandle = new AutoResetEvent(false);public static void DoWork(){_waitHandle.WaitOne(); // 等待信号// 继续执行}public static void Signal(){_waitHandle.Set(); // 发送信号}
6. Mutex 类
底层操作:
-
基于内核对象的互斥体实现。
-
支持跨进程同步。
特点:
-
重量级,性能较低。
-
支持递归锁(同一线程可以多次获取锁)。
-
适用于跨进程的同步。
适用场景:
-
跨进程的线程同步。
private static Mutex _mutex = new Mutex();public static void DoWork(){_mutex.WaitOne();try{// 临界区代码}finally{_mutex.ReleaseMutex();}}
7. Semaphore 类
底层操作:
-
基于内核对象的信号量实现。
-
使用
WaitOne和Release方法控制资源的访问。
特点:
-
允许多个线程同时访问资源,但数量有限。
-
支持跨进程同步。
-
性能较低,因为涉及内核模式的切换。
适用场景:
-
限制资源访问的场景(如连接池、线程池)。
private static Semaphore _semaphore = new Semaphore(3, 3); // 允许3个线程同时访问public static void DoWork(){_semaphore.WaitOne();try{// 临界区代码}finally{_semaphore.Release();}}
8. Events 类
底层操作:
-
基于内核对象的事件实现。
-
使用
Set和Reset方法控制信号状态。
特点:
-
支持手动重置和自动重置事件。
-
适用于线程间的信号通知。
-
性能较低,因为涉及内核模式的切换。
适用场景:
-
线程间的信号通知,复杂的同步逻辑。用于线程间的信号通知,类似于
WaitHandle,但更灵活。
private static ManualResetEventSlim _event = new ManualResetEventSlim(false);public static void DoWork(){_event.Wait(); // 等待信号// 继续执行}public static void Signal(){_event.Set(); // 发送信号}
9. Barrier 类
底层操作:
-
使用
SpinWait和内核事件结合实现。 -
允许多个线程在某个点同步,等待所有线程到达后再继续执行。
特点:
-
适用于分阶段的并行任务。
-
性能较高,因为大部分时间使用自旋等待。
适用场景:
-
多个线程在某个点同步的场景(如并行计算)。用于多个线程在某个点同步,等待所有线程到达后再继续执行。
总结:private static Barrier _barrier = new Barrier(3); // 等待3个线程public static void DoWork(){// 执行一些工作_barrier.SignalAndWait(); // 等待其他线程// 继续执行}
Barrier 适用于需要多个线程在某个点同步的场景,确保所有线程都到达后再继续执行。
10. ReaderWriterLockSlim 类
底层操作:
-
使用自旋和内核事件结合实现。
-
支持读写分离,允许多个读线程同时访问,但写线程独占访问。
特点:
-
适用于读写分离的场景。
-
性能较高,因为读操作可以并发执行。
-
支持递归锁。
适用场景:
-
读写分离的同步场景(如缓存、配置文件)。用于读写分离的同步场景,允许多个读线程同时访问,但写线程独占访问。
private static ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();public static void Read(){_rwLock.EnterReadLock();try{// 读取操作}finally{_rwLock.ExitReadLock();}}public static void Write(){_rwLock.EnterWriteLock();try{// 写入操作}finally{_rwLock.ExitWriteLock();}}
总结:ReaderWriterLockSlim 适用于读写分离的场景,提高了读操作的并发性能。
总结
-
lock:简单易用,适用于大多数同步场景。 -
Interlocked:高效的原子操作,适用于简单的数值操作。 -
Monitor:提供更细粒度的控制,适用于复杂同步逻辑。 -
SpinLock:适用于短时间的等待,避免线程上下文切换。 -
WaitHandle:线程间的信号通知,适用于复杂同步场景。 -
Mutex:跨进程的同步,性能较低。 -
Semaphore:限制资源访问,允许多个线程同时访问。 -
Events:灵活的线程间信号通知机制。 -
Barrier:多个线程在某个点同步。 -
ReaderWriterLockSlim:读写分离的同步场景,提高读操作的并发性能。
根据具体场景选择合适的同步机制,可以提高程序的性能和可靠性。
总结对比
| 同步机制 | 底层实现 | 性能 | 适用场景 | 跨进程支持 | 递归锁支持 |
|---|---|---|---|---|---|
lock |
基于 Monitor |
中等 | 简单的临界区保护 | 不支持 | 支持 |
Interlocked |
CPU 原子指令 | 高 | 简单的原子操作 | 不支持 | 不支持 |
Monitor |
基于 SyncBlock |
中等 | 复杂的同步逻辑 | 不支持 | 支持 |
SpinLock |
自旋等待 | 高 | 短时间的临界区保护 | 不支持 | 不支持 |
WaitHandle |
内核对象(事件、信号量等) | 低 | 线程间的信号通知 | 支持 | 不支持 |
Mutex |
内核对象(互斥体) | 低 | 跨进程的线程同步 | 支持 | 支持 |
Semaphore |
内核对象(信号量) | 低 | 限制资源访问 | 支持 | 不支持 |
Events |
内核对象(事件) | 低 | 线程间的信号通知 | 支持 | 不支持 |
Barrier |
自旋等待 + 内核事件 | 高 | 多个线程在某个点同步 | 不支持 | 不支持 |
ReaderWriterLockSlim |
自旋等待 + 内核事件 | 高 | 读写分离的同步场景 | 不支持 | 支持 |
选择建议:
-
高性能场景:优先选择
Interlocked、SpinLock或ReaderWriterLockSlim。 -
简单同步:使用
lock或Monitor。 -
复杂同步:使用
WaitHandle、Events或Barrier。 -
跨进程同步:使用
Mutex或Semaphore。
![]() |
Austin Liu 刘恒辉
Project Manager and Software Designer E-Mail:lzhdim@163.com Blog:https://lzhdim.cnblogs.com 欢迎收藏和转载此博客中的博文,但是请注明出处,给笔者一个与大家交流的空间。谢谢大家。 |




浙公网安备 33010602011771号