EventWaitHandle
事件等待句柄允许线程通过彼此发送信号和等待彼此的信号来同步活动。 这些同步事件是基于 Win32 等待句柄的,可分为两种类型:一种收到信号时自动重置;另一种需手动重置。
AutoResetEvent
AutoResetEvent 类表示一个本地等待处理事件,在释放了单个等待线程以后,该事件会在终止时自动重置。
Mutex
可以使用 Mutex
对象提供对资源的独占访问。 Mutex
类比 Monitor
类使用更多系统资源,但是它可以跨应用程序域边界进行封送处理,可用于多个等待,并且可用于同步不同进程中的线程。
互锁操作
Interlocked
类提供这样一些方法,即同步对多个线程共享的变量的访问的方法。 如果该变量位于共享内存中,则不同进程的线程就可以使用该机制。 互锁操作是原子的,即整个操作是不能由相同变量上的另一个互锁操作所中断的单元
读取器/编写器锁
ReaderWriterLockSlim 类允许多个线程同时读取一个资源,但在向该资源写入时要求线程等待以获得独占锁。
Semaphore 和 SemaphoreSlim
同步基元概述
.NET Framework 提供了一系列同步基元来控制线程交互并避免争用条件。 这可大致分为三个类别:锁定、通知和联锁操作
线程同步是协作这一点非常重要。 只要有一个线程避开同步机制直接访问受保护的资源,该同步机制就不是有效的。
锁定
锁向一个线程一次提供一个资源的控制功能,或者向指定数目的线程提供此功能。 请求正在使用中的独占锁的线程会被阻止,直到该锁变为可用为止。
-
独占锁
锁定的最简单的形式是 lock 语句该语句可控制对代码块的访问。 这种块通常称为临界区。 lock 语句使通过使用 Monitor 类的 Enter 和 Exit 方法实现的,它使用 try catch finally 确保该锁被释放。
-
Monitor类
如果用于锁的对象派生自 MarshalByRefObject,则 Monitor 类可在多个应用程序域中提供锁定。
-
Mutex 类
与 Monitor 类不同,mutex 可以是局部的,也可以是全局的。 全局 mutex(也称为命名的 mutex)在整个操作系统中可见,可用于在多个应用程序域或进程中同步线程。 局部 mutex 派生自 MarshalByRefObject,可以跨应用程序域边界使用。
Mutex 派生自 WaitHandle,这意味着它可用于 WaitHandle 提供的通知机制,如 WaitAll、WaitAny 和 SignalAndWait 方法。
-
SpinLock 类
从 .NET Framework 4开始,当 Monitor 所需的开销会造成性能下降时,可以使用 SpinLock 类。 当 SpinLock 遇到锁定的临界区时,它只是反复地旋转(重复检查的循环中等待),直至锁变为可用的。 如果锁保留的时间非常短,则旋转可比阻塞提供更好的性能。 但是,如果锁保留数十个周期以上,则 SpinLock 的表现会和 Monitor 一样,只不过将使用更多的 CPU 周期,因此会降低其他线程或进程的性能。
-
其他锁
锁不必是独占的。 允许有限数目的线程并发访问某个资源通常十分有用。 信号量和读写器锁旨在控制此类池资源访问。
-
ReaderWriterLock 类
如果编写器未处于活动状态,则任何数量的读取器均可以访问该资源。 当某个线程请求独占访问时,后续读取器请求将被阻止,直至所有现有的读取器都已退出该锁,并且编写器也已进入并退出该锁
-
Semaphore 类
Semaphore 类允许指定数目的线程访问某个资源。 请求该资源的其他线程会一直阻止,直到某个线程释放信号量为止。
与 Mutex 类一样,Semaphore 派生自 WaitHandle。 Semaphore 也与 Mutex 一样,可以是局部的,也可以是全局的。 它可以跨应用程序域边界使用。
与 Monitor、Mutex 和 ReaderWriterLock 不一样,Semaphore 不具有线程关联。 这意味着它可以用于一个线程获取信号量而另一个线程释放该信号量的情形。
通知
等待来自另一个线程的信号的最简单的方法是调用 Join 方法,该方法将进行阻塞,直至其他线程完成
-
等待句柄
等待句柄派生自 WaitHandle 类,后者又派生自 MarshalByRefObject。 因此,等待句柄可用于跨应用程序域边界同步线程的活动。
-
关卡
利用 Barrier 类,可以对多个线程进行循环同步,以便它们都在同一个点上阻塞并等待所有其他线程完成。 对于一个或多个线程在继续某个算法的下一阶段之前需要另一个线程的结果的情况,关卡很有用。
联锁操作
联锁操作是由 Interlocked
类的静态方法对某个内存位置执行的简单原子操作。 这些原子操作包括添加、递增和递减、交换、依赖于比较的条件交换,以及 32 位平台上的 64 位值的读取操作。
原子性的保证仅限于单个操作;如果必须将多个操作作为一个单元执行,则必须使用更粗粒度的同步机制。