Semaphore 和 SemaphoreSlim

System.Threading.Semaphore 类表示一个命名(系统范围内)或本地信号量。 它是环绕 Win32 信号量对象的精简包装器。 Win32 信号量是计数信号量,该可用于控制对资源池的访问。

SemaphoreSlim 类表示一个轻型快速信号灯,当等待时间预计非常短时,该信号灯可用于在单个进程中等待。 SemaphoreSlim 尽可能依赖于公共语言运行时 (CLR) 提供的同步基元。 但是,它还提供延迟初始化、基于内核的等待句柄,作为在多个信号量上进行等待的必要支持。 SemaphoreSlim 也支持使用取消标记,但不支持命名信号量或使用用于同步的等待句柄。

管理有限资源

线程通过调用从 WaitOne 类中继承的 WaitHandle 方法进入信号量,无论对于 System.Threading.Semaphore 对象、SemaphoreSlim.WaitSemaphoreSlim.WaitAsync 方法还是 SemaphoreSlim 对象都适用。 当调用返回时,信号量计数会减少。 当线程请求进入且计数为零时,线程会阻塞。 当线程通过调用 Semaphore.ReleaseSemaphoreSlim.Release 方法释放信号灯时,被阻塞的线程就会被允许进入。 受阻线程进入信号量无保证的顺序,比如先进先出 (FIFO) 或按后进先出 (LIFO)。

一个线程可通过反复调用 System.Threading.Semaphore 对象的 WaitOne 方法或 SemaphoreSlim 对象的 Wait 方法进入信号量。 若要释放信号量,线程可以调用 Semaphore.Release()SemaphoreSlim.Release() 方法重载相同的次数,或者调用 Semaphore.Release(Int32)SemaphoreSlim.Release(Int32) 方法重载并指定要释放的条目数。

信号量和线程标识

这两种信号量类型不会对调用WaitOneWaitReleaseSemaphoreSlim.Release方法强制实施线程标识。 例如,信号灯的常见使用方案涉及生成者线程和使用者线程,其中一个线程始终递增信号量计数,另一个线程始终递减信号量。

程序员有责任确保线程不会释放信号灯太多次。 例如,假定信号量的最大计数为 2 并且线程 A 和线程 B 都进入了该信号量。 如果线程 B 中的编程错误导致它调用 Release 两次,则两个调用都成功。 信号灯计数已满,当线程 A 最终调用 Release 时,SemaphoreFullException 抛出。

命名信号量

Windows作系统允许信号灯具有名称。 命名信号量是系统范围的。 也就是说,创建命名信号灯后,所有进程中的所有线程都可以看到它。 因此,命名信号灯可用于同步进程和线程的活动。

可以使用指定名称的构造函数之一创建一个 Semaphore 对象,该对象表示命名的系统信号灯。

注释

由于命名信号量是系统范围内的,因此可以有多个 Semaphore 对象表示同一个命名信号量。 每次调用构造函数或 Semaphore.OpenExisting 方法时,都会创建一个新 Semaphore 对象。 重复指定同一名称会创建表示同一命名信号灯的多个对象。

使用命名信号量时务必谨慎。 因为它们是系统范围的,使用同一名称的另一进程可能会意外地进入你的信号量。 在同一台计算机上执行的恶意代码可以使用此代码作为拒绝服务攻击的基础。

使用访问控制安全性来保护 Semaphore 表示命名信号灯的对象,最好是使用指定 System.Security.AccessControl.SemaphoreSecurity 对象的构造函数。 还可以使用 Semaphore.SetAccessControl 此方法应用访问控制安全性,但这会在创建信号灯的时间和受保护时间之间留下漏洞窗口。 使用访问控制安全机制来保护信号量有助于阻止恶意攻击,但不能解决的意外的名称冲突问题。

另请参阅