信号量是如何从另一个线程释放的



据我所知,线程可以释放信号量,而无需首先使用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

您可以将信号量视为阻塞队列的特殊情况:信号量的"计数"是队列中的项目数,但项目本身不包含任何信息。正如允许任何线程将一个项目放入阻塞队列,允许任何线程取出一个项目一样,任何线程都可以递增或递减信号量的计数。

最新更新