[多线程]线程和进程间同步
同步概念
.NET 提供了一系列可用于同步对共享资源或协调线程交互的访问的类型。多个 .NET 同步基元派生自 System.Threading.WaitHandle 类,该类会封装本机操作系统同步句柄并将信号机制用于线程交互。
- System.Threading.Mutex,授予对共享资源的独占访问权限。 如果没有任何线程拥有它,则 mutex 将处于已发出信号状态。
- System.Threading.Semaphore,限制可同时访问某一共享资源或资源池的线程数。 当信号量计数大于零时,会将信号量的状态设置为已发出信号;当信号量计数为零时,会将信号量的状态设置为未发出信号。
- System.Threading.EventWaitHandle,表示线程同步事件,可以处于已发出信号状态或未发出信号状态。
- System.Threading.AutoResetEvent,派生自 EventWaitHandle,当发出信号时,会在发布单个等待线程后自动重置为未发出信号状态。
- System.Threading.ManualResetEvent,派生自 EventWaitHandle,当发出信号时,会保持已发出信号状态,直到调用 Reset 方法。
在 .NET Framework 中,由于 WaitHandle 派生自 System.MarshalByRefObject,因此,这些类型可用于跨应用程序域边界同步线程的活动。
以下类型可以表示已命名的系统同步句柄,这些句柄在整个操作系统中都可见并可用于进程间同步:
- Mutex(.NET Framework 和 .NET Core)
- Semaphore(.NET Framework 和 Windows 上的 .NET Core)
- EventWaitHandle(.NET Framework 和 Windows 上的 .NET Core)。
轻量同步类型不依赖于基础操作系统句柄,通常会提供更好的性能。 但是,它们不能用于进程间同步。 将这些类型用于一个应用程序中的线程同步。
- SemaphoreSlim 是Semaphore 的轻量替代项。
同步作用
同步对共享资源的访问
Monitor 类
System.Threading.Monitor 类通过获取或释放用于标识资源的对象上的 lock 来授予对共享资源的相互独占访问权限。由于 Monitor 类具有线程关联,因此获取了 lock 的线程必须通过调用 Monitor.Exit 方法来释放 lock。可以通过使用 Monitor.Wait、Monitor.Pulse 和 Monitor.PulseAll 方法来协调用于获取同一对象上的 lock 的线程的交互。
Mutex 类
System.Threading.Mutex 类(与 Monitor 类似),授予对共享资源的独占访问权限。 使用 Mutex.WaitOne 方法重载之一请求 mutex 的所有权。 Mutex(与 Monitor 类似)具有线程关联,并且已获取 mutex 的线程必须通过调用 Mutex.ReleaseMutex 方法来释放它。
Mutex 类(与 Monitor 不同)可用于进程间同步。 为此,请使用命名 mutex,它在整个操作系统中都可见。 若要创建命名 mutex 实例,请使用指定了名称的 Mutex 构造函数。 还可以调用 Mutex.OpenExisting 方法来打开现有的命名系统 mutex。
SpinLock 结构
System.Threading.SpinLock 结构(类似于 Monitor),基于 lock 的可用性授予对共享资源的独占访问权限。
ReaderWriterLockSlim 类
System.Threading.ReaderWriterLockSlim 类授予对共享资源的独占访问权限以便进行写入,并允许多个线程同时访问资源以便进行读取。
Semaphore 和 SemaphoreSlim 类
System.Threading.Semaphore 和 System.Threading.SemaphoreSlim 限制可同时访问某一共享资源或资源池的线程数。 请求资源的其他线程将等待,直到任何线程释放信号量。 由于信号量没有线程关联,因此一个线程可以获取信号量,而另一个线程可以释放它。
在 Windows 上,可以将 Semaphore 用于进程间同步。 为此,通过使用指定了名称或 Semaphore.OpenExisting 方法的 Semaphore 构造函数之一来创建表示指定了已命名系统信号量的 Semaphore 实例。 SemaphoreSlim 不支持已命名系统信号量。
线程交互
线程交互(或线程信号)表示线程必须等待来自一个或多个线程的通知或信号才能继续。
EventWaitHandle、AutoResetEvent、ManualResetEvent 和 ManualResetEventSlim 类
System.Threading.EventWaitHandle 类表示一个线程同步事件。
在 Windows 上,可以将 EventWaitHandle 用于进程间同步。 为此,通过使用指定了名称或 EventWaitHandle.OpenExisting 方法的 EventWaitHandle 构造函数之一来创建表示指定了已命名系统信号量的 EventWaitHandle 实例。
Interlocked 类
System.Threading.Interlocked 类提供了可对变量执行简单原子操作的静态方法。 这些原子操作包括添加、递增和递减、交换、取决于比较的条件交换以及读取 64 位整数值的操作。
SpinWait 结构
System.Threading.SpinWait 结构为基于自旋的等待提供支持。
CountdownEvent 类
System.Threading.CountdownEvent 类表示当其计数为零时将被设置的事件。 当 CountdownEvent.CurrentCount 大于零时,调用了 CountdownEvent.Wait 的线程会被阻止。 调用 CountdownEvent.Signal 会递减事件的计数。
与可用于通过来自一个线程的信号取消阻止多个线程的 ManualResetEvent 或 ManualResetEventSlim 相反,你可以使用 CountdownEvent 通过来自多个线程的信号取消阻止一个或多个线程。
Barrier 类
System.Threading.Barrier 类表示线程执行屏障。 调用了 Barrier.SignalAndWait 方法的线程发出信号,它已到达屏障并等待其他参与线程到达屏障。 当所有参与线程到达屏障时,它们将继续前进,屏障将进行重置,使之再次可用。
在继续执行下一个计算阶段之前,当一个或多个线程需要其他线程的结果时,可以使用 Barrier。