C# 信号量是如何从另一个线程释放的?
据我所知,线程可以在信号量上释放,而无需首先使用C# 信号量是如何从另一个线程释放的?,c#,multithreading,semaphore,C#,Multithreading,Semaphore,据我所知,线程可以在信号量上释放,而无需首先使用WaitOne获取锁 因此,如果我们有线程A、B和C以及一个信号量,A和B调用WaitOne,获得一个锁并开始处理它们的事务。 线程C出现,只需在信号量上调用Release 这将使信号量的计数增加1。这是否意味着信号量将终止A或B,或者只允许第三个线程获取锁并在其池中有3个线程,即使最大值为2?您可以将信号量视为阻塞队列的一种特殊情况:信号量的“计数”是队列中的项目数,但这些项目本身不包含任何信息。正如任何线程都可以将项目放入阻塞队列,任何线程都可
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
),如果为true-如果不进入队列,请随意执行
使信号量如此混乱的是,CurrentCount
值可以减小或增大
- 每次调用
Wait() 线程,这意味着可用的执行槽少了一个 并且正在执行一个线程
- 每次发布
时,其增加一个(或多个) 在代码中的其他地方调用,这意味着还有一个 可用的执行槽,因此信号量中的第一个线程位于 正在执行队列(它不会终止其他队列)版本(1)
release(1)
增加CurrentCount
来释放另一个执行槽
信号量不是活动对象;它不控制任何代码的运行。程序员有责任以合理的方式使用它。如果这意味着增加互斥,那是另一个问题——一个简单的计数信号量不能解决这个问题。线程C恶意释放了一个它不拥有的资源,这是有缺陷的。监视器是一个更高级别的抽象,没有这个问题,这就是为什么.NET代码中的原始信号量非常少的原因。对于更简单的场景,当只需要线程安全计数器时,会有
联锁的
。在执行线程之前,重置两个信号量。然后在末尾的线程内设置信号量。所以C除了等待两个信号量被设置外,什么也不做。@jdweng,让我们假设我没有遵循好的编程实践,试图为互联网上关于这类事情的特定问题提供一个理论场景。@JeroenMostert,这是否意味着我在这个问题上的假设是正确的?是的,但和通常的情况一样,说服自己的最好方法是制作一个演示问题的小玩具应用程序(像LINQPad这样的工具使这变得非常容易)。我认为这比互联网上任何随机的人的评论都要好(当然更具洞察力)。虽然显示线程问题有时可能很棘手(有时没有任何完全不可能的问题),但出于演示目的,在必要的地方插入一些。睡眠通常会起作用。你的答案很好,但Jeroen Mostert在评论中给出了回答我问题的关键点,这是“信号量不是活动对象”,也就是说,信号量不控制代码的执行,它只是一个简单的同步构造,让事情通过它,但之后它们会做什么-它不在乎。因为最初我认为它控制它所传递的任何线程的执行,就像监视器一样(在某种意义上,监视器非常关心进入它的线程发生了什么,因为它需要释放它)@ShaharShokrani,这是大多数编程语言和库中大多数同步对象的典型特征:它们只起到门的作用,在满足某些条件之前阻止线程通过。为了防止数据结构损坏,程序员必须设置一个门,然后它就成为程序员的责任ty以确保任何代码都不会修改数据,除非数据已通过闸门。
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