据我所知,线程可以释放信号量,而无需首先使用WaitOne
获取锁。
因此,如果我们有线程A、B和C以及一个信号量,A和B调用WaitOne
,获得一个锁并开始执行它们的业务。
随之而来的是线程C,它只是在信号量上调用Release
。
这应该使信号量的计数增加1。这是否意味着信号量将终止A或B,或者只允许第三个线程获取锁并在其池中有3个线程,即使最大值为2?
考虑
var semaphore = new SemaphoreSlim(2);
这意味着此时信号量只有2个执行槽,但您必须记住,这只是执行槽的初始数量(用于同时授予请求)。
因此,如果我们将A、B、C线程派生到一个有2个执行槽的信号量中,则前两个线程将被执行,C线程将被排队,直到代码中的其他人向信号量发出信号,表示可以再添加一个执行槽。
当有人说可以执行队列中的下一个线程时,无论其他线程如何,都会执行C。
一些技术示例:
(正如我在@dmitri nesteruk的课程中看到的)
可用执行槽的总数由CurrentCount
表示。
每次线程想要执行时,它都会询问信号量是否有可用的执行槽(带有CurrentCount > 0
),如果是真的,请随意执行,如果没有进入队列。
使信号量如此混乱的是CurrentCount
值可以减少也可以增加。
每次
Wait()
被线程,这意味着可用的执行槽减少了一个并且a线程正在被执行。每次
Release(1)
在代码中的其他地方调用,这意味着还有一个可用的执行槽,因此信号量中的第一个线程位于队列正在执行(它不会终止其他队列)。
在这个例子中,我们生成了3个线程,但只有前两个线程会被执行,直到有人对信号量说,他可以通过用Release(1)
增加CurrentCount
来释放另一个执行槽。
for (int i = 0; i < 3 ; i++)
{
Task.Factory.StartNew(() =>
{
Console.WriteLine($"Spawning task: {Task.CurrentId}");
semaphore.Wait(); //CurrentCount--
Console.WriteLine($"Executing task: {Task.CurrentId}");
});
}
while (semaphore.CurrentCount <= 2)
{
Console.ReadKey();
Console.WriteLine("Key pressed");
semaphore.Release(1); //CurrentCount++
}
输出:
Spawning task: A
Spawning task: B
Spawning task: C
Executing task: A
Executing task: B
.....
Key pressed
Executing task: C
您可以将信号量视为阻塞队列的特殊情况:信号量的"计数"是队列中的项目数,但项目本身不包含任何信息。正如允许任何线程将一个项目放入阻塞队列,允许任何线程取出一个项目一样,任何线程都可以递增或递减信号量的计数。