Semaphore 和 SemaphoreSlimSemaphore and SemaphoreSlim

System.Threading.Semaphore 类表示一个命名(系统范围内)或本地信号量。The System.Threading.Semaphore class represents a named (systemwide) or local semaphore. 它是环绕 Win32 信号量对象的精简包装器。It is a thin wrapper around the Win32 semaphore object. Win32 信号量是计数信号量,该可用于控制对资源池的访问。Win32 semaphores are counting semaphores, which can be used to control access to a pool of resources.

SemaphoreSlim 类表示一个轻量、快速的信号量,可在等待时间预计很短的情况下用于在单个进程内等待。The SemaphoreSlim class represents a lightweight, fast semaphore that can be used for waiting within a single process when wait times are expected to be very short. SemaphoreSlim 尽可能多地依赖公共语言运行时 (CLR) 提供的同步基元。SemaphoreSlim relies as much as possible on synchronization primitives provided by the common language runtime (CLR). 但是,它还提供延迟初始化、基于内核的等待句柄,作为在多个信号量上进行等待的必要支持。However, it also provides lazily initialized, kernel-based wait handles as necessary to support waiting on multiple semaphores. SemaphoreSlim 也支持使用取消标记,但不支持命名信号量或使用用于同步的等待句柄。SemaphoreSlim also supports the use of cancellation tokens, but it does not support named semaphores or the use of a wait handle for synchronization.

管理有限资源Managing a Limited Resource

线程通过调用从 WaitOne 类中继承的 WaitHandle 方法进入信号量,无论对于 System.Threading.Semaphore 对象、SemaphoreSlim.WaitSemaphoreSlim.WaitAsync 方法还是 SemaphoreSlim 对象都适用。Threads enter the semaphore by calling the WaitOne method, which is inherited from the WaitHandle class, in the case of a System.Threading.Semaphore object, or the SemaphoreSlim.Wait or SemaphoreSlim.WaitAsync method, in the case of a SemaphoreSlim object. 当调用返回时,信号量计数会减少。When the call returns, the count on the semaphore is decremented. 当线程请求进入且计数为零时,此线程受到阻止。When a thread requests entry and the count is zero, the thread blocks. 线程通过调用 Semaphore.ReleaseSemaphoreSlim.Release 方法释放信号量时,允许受阻线程进入。As threads release the semaphore by calling the Semaphore.Release or SemaphoreSlim.Release method, blocked threads are allowed to enter. 受阻线程进入信号量无保证的顺序,比如先进先出 (FIFO) 或按后进先出 (LIFO)。There is no guaranteed order, such as first-in, first-out (FIFO) or last-in, first-out (LIFO), for blocked threads to enter the semaphore.

一个线程可通过反复调用 System.Threading.Semaphore 对象的 WaitOne 方法或 SemaphoreSlim 对象的 Wait 方法进入信号量。A thread can enter the semaphore multiple times by calling the System.Threading.Semaphore object's WaitOne method or the SemaphoreSlim object's Wait method repeatedly. 为了释放信号量,线程可以调用 Semaphore.Release()SemaphoreSlim.Release() 方法重载相同次数,或调用 Semaphore.Release(Int32)SemaphoreSlim.Release(Int32) 方法重载并指定要释放的项的数量。To release the semaphore, the thread can either call the Semaphore.Release() or SemaphoreSlim.Release() method overload the same number of times, or call the Semaphore.Release(Int32) or SemaphoreSlim.Release(Int32) method overload and specify the number of entries to be released.

信号量和线程标识Semaphores and Thread Identity

两种信号量类型不会在对 WaitOneWaitReleaseSemaphoreSlim.Release 方法的调用上强制线程标识。The two semaphore types do not enforce thread identity on calls to the WaitOne, Wait, Release, and SemaphoreSlim.Release methods. 例如,信号量的一种常见使用方案涉及制造者线程和使用者线程,其中一个线程始终增加信号量计数,另一个始终减少信号量计数。For example, a common usage scenario for semaphores involves a producer thread and a consumer thread, with one thread always incrementing the semaphore count and the other always decrementing it.

程序员有责任确保线程不会过多次地地释放信号量。It is the programmer's responsibility to ensure that a thread does not release the semaphore too many times. 例如,假定信号量的最大计数为 2 并且线程 A 和线程 B 都进入了该信号量。For example, suppose a semaphore has a maximum count of two, and that thread A and thread B both enter the semaphore. 如果线程 B 中的编程错误导致它两次成功调用了 ReleaseIf a programming error in thread B causes it to call Release twice, both calls succeed. 信号灯计数已满,当线程 A 最终调用 Release 时,SemaphoreFullException 抛出。The count on the semaphore is full, and when thread A eventually calls Release, a SemaphoreFullException is thrown.

命名信号量Named Semaphores

Windows 操作系统允许信号量拥有名称。The Windows operating system allows semaphores to have names. 命名信号量是系统范围的。A named semaphore is system wide. 即,一旦创建了命名信号量,它对所有进程中的所有线程均可见。That is, once the named semaphore is created, it is visible to all threads in all processes. 因此,命名信号量可用于同步进程以及线程的活动。Thus, named semaphore can be used to synchronize the activities of processes as well as threads.

你可以通过使用指定了名称的构造函数之一来创建表示指定了已命名系统信号量的 Semaphore 对象。You can create a Semaphore object that represents a named system semaphore by using one of the constructors that specifies a name.


因为命名信号量是系统范围的,所以它可能有多个 Semaphore 对象,这些对象表示同一命名信号量。Because named semaphores are system wide, it is possible to have multiple Semaphore objects that represent the same named semaphore. 每次调用构造函数或 Semaphore.OpenExisting 方法都会创建一个新的 Semaphore 对象。Each time you call a constructor or the Semaphore.OpenExisting method, a new Semaphore object is created. 反复指定相同的名称会创建多个表示同一命名信号量的对象。Specifying the same name repeatedly creates multiple objects that represent the same named semaphore.

使用命名信号量时务必谨慎。Be careful when you use named semaphores. 因为它们是系统范围的,使用同一名称的另一进程可能会意外地进入你的信号量。Because they are system wide, another process that uses the same name can enter your semaphore unexpectedly. 同一台计算机上的恶意代码执行可将此作为拒绝服务攻击的基础。Malicious code executing on the same computer could use this as the basis of a denial-of-service attack.

使用访问控制安全性来保护表示命名信号量的 Semaphore 对象,最好通过使用指定 System.Security.AccessControl.SemaphoreSecurity 对象的构造函数。Use access control security to protect a Semaphore object that represents a named semaphore, preferably by using a constructor that specifies a System.Security.AccessControl.SemaphoreSecurity object. 你还可以通过使用 Semaphore.SetAccessControl 方法应用访问控制安全,但这会使窗口在创建信号量的时间以及信号量受到保护的时间之间留下漏洞。You can also apply access control security using the Semaphore.SetAccessControl method, but this leaves a window of vulnerability between the time the semaphore is created and the time it is protected. 使用访问控制安全机制来保护信号量有助于阻止恶意攻击,但不能解决的意外的名称冲突问题。Protecting semaphores with access control security helps prevent malicious attacks, but does not solve the problem of unintentional name collisions.

另请参阅See also