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

How does a semaphore release from another thread?

据我所知,线程可以释放信号量而无需先使用 WaitOne 获取锁。

所以,如果我们有线程 A、B 和 C 以及一个信号量,A 和 B 调用 WaitOne,获取锁并开始处理它们的业务。
线程 C 随之而来,只是在信号量上调用 Release

这应该将信号量的计数增加 1。这是否意味着信号量将终止 A 或 B,或者只允许第三个线程获取锁并在其池中有 3 个线程,即使最大值为 2?

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

考虑

var semaphore = new SemaphoreSlim(2);

这意味着此时的信号量只有2个执行槽,但你要记住它只是初始数量个执行槽(对于请求同时被授予)。

因此,如果我们将 A、B、C 线程生成到具有 2 个执行槽的信号量中,将执行前两个线程,并且 C 线程将排队等待代码中的其他人向信号量发出信号确定再添加一个执行槽。

当有人说可以执行队列中的下一个线程时,C 将被执行,而不管其他线程。


一些技术示例:

(正如我在@dmitri-nesteruk 的课程中​​所见)

可用 个执行槽的总数由 CurrentCount 表示。

每次想要执行一个线程时,它都会询问信号量它是否有可用的执行槽(CurrentCount > 0),如果为真 - 如果没有进入队列,请随意执行。

使信号量如此混乱的原因是 CurrentCount 值可以减少或增加。

  • 每次 Wait() 被调用时它减一 线程,这意味着少了一个可用的执行槽 并且正在执行一个线程。

  • 每次 Release(1) 增加一(或更多) 在代码的其他地方调用,这意味着还有一个 可用的执行槽所以第一个线程在信号量里面 queue 正在执行(不会终止其他队列)。

在这个例子中,我们产生了 3 个线程,但只有前两个线程会被执行,直到有人对信号量说他可以通过将 CurrentCount 增加到 Release(1) 来释放另一个执行槽.

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