同步基元概述

.NET 提供了一系列类型,可用于同步对共享资源或坐标线程交互的访问。

重要

使用相同的同步基元实例来保护共享资源的访问。 如果使用不同的同步基元实例来保护同一资源,则会绕过同步基元提供的保护。

WaitHandle 类和轻型同步类型

多个 .NET 同步基元派生自System.Threading.WaitHandle类, 该类封装了一个本机操作系统同步句柄,并使用信号机制进行线程交互。 这些课程包括:

在 .NET Framework 中,由于 WaitHandle 派生自 System.MarshalByRefObject,因此这些类型可用于跨应用程序域边界同步线程的活动。

在 .NET Framework、.NET Core 和 .NET 5+ 中,其中一些类型可以表示命名的系统同步句柄,这些句柄在整个作系统中可见,并可用于进程间同步:

有关详细信息,请参阅 WaitHandle API 参考。

轻量级同步类型不依赖于底层操作系统句柄,通常可提供更好的性能。 但是,它们不能用于进程间同步。 将这些类型用于一个应用程序中的线程同步。

其中一些类型是派生自 WaitHandle的类型的替代方法。 例如, SemaphoreSlim 是一种轻型替代方法 Semaphore

对共享资源的访问同步

.NET 提供了一系列同步基元,用于控制多个线程对共享资源的访问。

Monitor 类

System.Threading.Monitor 通过获取或释放标识资源的对象上的锁,授予对共享资源的互斥访问权限。 锁定时,持有锁的线程可以再次获取并释放锁。 阻止任何其他线程获取锁,方法 Monitor.Enter 将等待锁定释放。 Enter 方法可获取释放的 lock。 还可以使用 Monitor.TryEnter 该方法指定线程尝试获取锁的时间量。 Monitor由于类具有线程相关性,因此获取锁的线程必须通过调用Monitor.Exit该方法释放锁。

可以使用Monitor.WaitMonitor.PulseMonitor.PulseAll方法在获取同一对象锁的线程之间进行交互协调。

有关详细信息,请参阅 Monitor API 参考。

注释

使用 C# 中的 lock 语句和 Visual Basic 中的 SyncLock 语句来同步对共享资源的访问,而不是直接使用 Monitor 类。 这些语句是通过使用 EnterExit 方法和 try…finally 块实现的,以确保始终释放获取的锁。

Mutex 类

类似于 System.Threading.MutexMonitor 类授予对共享资源的独占访问权限。 使用 Mutex.WaitOne 方法重载之一请求 mutex 的所有权。 像 Monitor 一样,Mutex 具有线程亲和性,并且获取互斥体的线程必须通过调用 Mutex.ReleaseMutex 方法来释放它。

与此类不同 MonitorMutex 类可用于进程间同步。 为此,请使用命名 mutex,它在整个操作系统中都可见。 若要创建命名 mutex 实例,请使用指定了名称的 Mutex 构造函数。 还可以调用 Mutex.OpenExisting 方法来打开现有的命名系统 mutex。

有关详细信息,请参阅 Mutex 一文和 Mutex API 参考。

SpinLock 结构

结构 System.Threading.SpinLock (例如 Monitor)根据锁的可用性授予对共享资源的独占访问权限。 尝试获取不可用的锁时 SpinLock ,它会在循环中等待,反复检查,直到锁可用。

有关使用旋转锁的优点和缺点的详细信息,请参阅 SpinLock 文章和 SpinLock API 参考。

ReaderWriterLockSlim 类

System.Threading.ReaderWriterLockSlim 类授予对共享资源进行写入的独占访问权限,并允许多个线程同时访问资源进行读取。 你可能想要使用 ReaderWriterLockSlim 来同步对支持线程安全读取操作的共享数据结构的访问,但写入操作需要独占访问权限。 当线程请求独占访问(例如,通过调用 ReaderWriterLockSlim.EnterWriteLock 方法)时,后续的读取器和编写器请求会被阻塞,直到所有现有的读取器退出锁定,并且编写器已进入并退出该锁定。

有关详细信息,请参阅 ReaderWriterLockSlim API 参考。

Semaphore 和 SemaphoreSlim 类

System.Threading.SemaphoreSystem.Threading.SemaphoreSlim类限制可同时访问共享资源或资源池的线程数。 请求资源的其他线程等待,直到任何线程释放信号灯。 由于信号灯没有线程相关性,一个线程可以获取信号灯,另一个线程可以释放它。

SemaphoreSlim 是一种轻型替代方法 Semaphore ,只能用于单个进程边界内的同步。

在 Windows 上,你可以使用 Semaphore 进行进程间同步。 为此,请使用指定名称的Semaphore之一或方法创建表示Semaphore.OpenExisting命名系统信号量的实例。 SemaphoreSlim 不支持已命名系统信号量。

有关详细信息,请参阅 Semaphore 和 SemaphoreSlim 文章以及 SemaphoreSemaphoreSlim API 参考。

线程交互或信号

线程交互(或线程信号)表示线程必须等待一个或多个线程的通知或信号才能继续。 例如,如果线程 A 调用 Thread.Join 线程 B 的方法,则线程 A 将被阻止,直到线程 B 完成。 上一部分中介绍的同步基元提供了一种不同的信号机制:通过释放锁,线程会通知另一个线程,它可以通过获取锁来继续。

本部分介绍 .NET 提供的其他信号构造。

EventWaitHandle、AutoResetEvent、ManualResetEvent 和 ManualResetEventSlim 类

System.Threading.EventWaitHandle 类表示线程同步事件。

同步事件可以处于未信号状态或已信号状态。 当事件的状态为未发出信号时,调用事件的WaitOne 重载的线程会被阻塞,直到事件发出信号。 该方法 EventWaitHandle.Set 将事件的状态设置为信号。

已发出信号的 EventWaitHandle 的行为取决于其重置模式:

在 Windows 上,你可以使用 EventWaitHandle 进行进程间同步。 为此,请使用指定名称或EventWaitHandle方法的 EventWaitHandle 构造函数之一创建表示EventWaitHandle.OpenExisting命名系统同步事件的实例。

有关详细信息,请参阅 EventWaitHandle 文章。 有关 API 参考,请参阅EventWaitHandleAutoResetEventManualResetEventManualResetEventSlim

CountdownEvent 类

System.Threading.CountdownEvent 类表示在其计数为零时设置的事件。 当 CountdownEvent.CurrentCount 大于零时,将阻止调用 CountdownEvent.Wait 的线程。 调用 CountdownEvent.Signal 以递减事件的计数。

ManualResetEventManualResetEventSlim(可以用一个线程的信号解除多个线程的阻塞)不同,你可以使用CountdownEvent用多个线程的信号解除一个或多个线程的阻塞。

有关详细信息,请参阅 CountdownEvent 文章和 CountdownEvent API 参考。

Barrier 类

System.Threading.Barrier 类表示线程执行屏障。 调用 Barrier.SignalAndWait 方法的线程指示它到达屏障,并等待其他参与者线程到达屏障。 当所有参与线程到达屏障时,它们将继续前进,屏障将进行重置,使之再次可用。

如果一个或多个线程在继续下一个计算阶段之前需要其他线程的结果,则可能会使用 Barrier

有关详细信息,请参阅 屏障 文章和 Barrier API 参考。

Interlocked 类

System.Threading.Interlocked 类提供对变量执行简单原子作的静态方法。 这些原子操作包括加法、递增和递减、交换和依赖于比较的条件交换,以及对 64 位整数值的读取操作。

有关详细信息,请参阅 Interlocked API 参考。

SpinWait 结构

System.Threading.SpinWait 结构为基于自旋的等待提供支持。 当线程必须等待事件发出信号或条件得以满足时,如果预期的实际等待时间短于使用等待句柄或其他方式阻塞线程所需的等待时间,可以考虑使用它。 通过使用 SpinWait,你可以指定等待期间要旋转的一小段时间,且只在特定时间不满足条件时让行(例如,通过等待或休眠)。

有关详细信息,请参阅 SpinWait 文章和 SpinWait API 参考。

另请参阅