信号量 - 初始计数有啥用?
Posted
技术标签:
【中文标题】信号量 - 初始计数有啥用?【英文标题】:Semaphore - What is the use of initial count?信号量 - 初始计数有什么用? 【发布时间】:2011-06-10 01:19:11 【问题描述】:http://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim.aspx
要创建信号量,我需要提供初始计数和最大计数。 MSDN 声明初始计数是 -
初始请求数 可以授予的信号量 同时。
虽然它声明最大计数是
请求的最大数量 可以授予的信号量 同时。
我可以理解,最大计数是可以同时访问资源的最大线程数。但是,初始计数有什么用?
如果我创建一个初始计数为 0 且最大计数为 2 的信号量,则我的线程池线程都无法访问该资源。如果我将初始计数设置为 1,最大计数设置为 2,那么只有线程池线程可以访问资源。只有当我将初始计数和最大计数都设置为 2 时,2 个线程才能同时访问资源。那么,我真的对初始计数的意义感到困惑吗?
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(0, 2); //all threadpool threads wait
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 2);//only one thread has access to the resource at a time
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(2, 2);//two threadpool threads can access the resource concurrently
【问题讨论】:
【参考方案1】:是的,当初始数字设置为 0 - 当您增加“CurrentCount”属性时,所有线程都将等待。您可以使用 Release() 或 Release(Int32) 来完成。
Release(...) - 将增加信号量计数器
Wait(...) - 将其递减
您不能增加计数器(“CurrentCount”属性)大于您在初始化时设置的最大计数。
例如:
SemaphoreSlim^ s = gcnew SemaphoreSlim(0,2); //s->CurrentCount = 0
s->Release(2); //s->CurrentCount = 2
...
s->Wait(); //Ok. s->CurrentCount = 1
...
s->Wait(); //Ok. s->CurrentCount = 0
...
s->Wait(); //Will be blocked until any of the threads calls Release()
【讨论】:
您的代码将更好地显示在答案中,而不是作为评论。 LOL 这可能是我第五次得到相同的答案,因为构造函数的文档总是让我对设置哪些值感到困惑。干杯【参考方案2】:那么,我真的对初始计数的意义感到困惑吗?
此处可能有帮助的重要一点是Wait
减少信号量计数,Release
增加它。
initialCount
是立即允许的资源访问次数。或者,换句话说,它是在信号量被实例化后立即可以在不阻塞的情况下调用Wait
的次数。
maximumCount
是信号量可以获得的最高计数。假设initialCount
计数为零,则可以调用Release
而不会引发异常的次数。如果initialCount
设置为与maximumCount
相同的值,则在信号量实例化后立即调用Release
将引发异常。
【讨论】:
这很有帮助!我一直在考虑信号量,因为 initialCount 是初始 BLOCKED 资源的数量,而不是立即可用的资源数量。谢谢。 @PhilipTenn,我同意 - 文档在这方面不清楚 我同意,他们应该更改该变量名称或更新文档 @Sandbox 你应该接受这个答案IMO,因为它确实解释了initialCount
参数的含义。【参考方案3】:
您希望有多少线程能够一次访问资源?将您的初始计数设置为该数字。如果该数字在程序的整个生命周期中永远不会增加,请将您的最大计数也设置为该数字。这样,如果您在释放资源的方式上出现编程错误,您的程序将会崩溃并通知您。
(有两个构造函数:一个只取初始值,一个额外取最大计数。使用合适的。)
【讨论】:
【参考方案4】:如果您希望在一段时间内没有线程访问您的资源,则将初始计数传递为 0,当您希望在创建信号量后立即授予对所有线程的访问权限时,您将初始计数的值传递为等于到最大计数。例如:
hSemaphore = CreateSemaphoreA(NULL, 0, MAX_COUNT, NULL) ;
//Do something here
//No threads can access your resource
ReleaseSemaphore(hSemaphore, MAX_COUNT, 0) ;
//All threads can access the resource now
正如 MSDN 文档中所引用的-“ReleaseSemaphore 的另一种用途是在应用程序的初始化期间。应用程序可以创建一个初始计数为零的信号量。这会将信号量的状态设置为非信号并阻止所有线程访问受保护的资源。当应用程序完成初始化时,它使用 ReleaseSemaphore 将计数增加到最大值,以允许正常访问受保护的资源。"
【讨论】:
对不起,我给了你C++的例子,虽然可以消除疑问。 关于初始化的引用非常有帮助,尽管其他答案解释了 semaphoreSlim 的工作原理,但这是唯一一个为这个有点晦涩的功能提供非常有用的示例用例的答案。【参考方案5】:这样,当当前线程创建信号量时,它可以从一开始就占用一些资源。
【讨论】:
那么,您的意思是当我希望两个工作线程访问资源时,我应该更改初始计数? 没有。声明计数的是当前线程。如果您不希望当前线程声明任何访问通道 0 或使用带有一个参数的重载。【参考方案6】:通常,当SemaphoreSlim
用作节流器时,initialCount
和maxCount
具有相同的值:
var semaphore = new SemaphoreSlim(maximumConcurrency, maximumConcurrency);
...semaphore
与此模式一起使用:
await semaphore.WaitAsync(); // or semaphore.Wait();
try
// Invoke the operation that must be throttled
finally
semaphore.Release();
initialCount
配置最大并发策略,maxCount
确保不会违反此策略。如果您省略第二个参数(maxCount
),您的代码也可以正常工作,前提是其中没有错误。如果有一个错误,并且每个WaitAsync
后面可能有多个Release
,那么maxCount
将有助于在它最终出现在您的程序的发布版本中之前检测到这个错误。该错误将以SemaphoreFullException
的形式出现,希望在测试预发布版本期间,因此您将能够在它造成任何真正伤害之前跟踪和消除它(在它导致违反最大生产环境中的并发策略)。
maxCount
参数的默认值为Int32.MaxValue
(source code)。
【讨论】:
优秀的答案,权威!【参考方案7】:正如MSDN 在备注部分中解释的那样:
如果 initialCount 小于 maximumCount,效果与当前线程调用 WaitOne (maximumCount 减去 initialCount) 次的效果相同。如果您不想为创建信号量的线程保留任何条目,请为 maximumCount 和 initialCount 使用相同的数字。
因此,如果初始计数为 0 且最大值为 2,则好像 WaitOne 已被主线程调用了两次,因此我们已达到容量(信号量计数现在为 0)并且没有线程可以进入信号量。类似地,如果初始计数为 1,最大值为 2,则 WaitOnce 已被调用一次,并且在我们再次达到容量之前只有一个线程可以进入,依此类推。
如果初始计数使用 0,我们总是可以调用 Release(2) 将信号量计数增加到最大值,以允许最大数量的线程获取资源。
【讨论】:
【参考方案8】:信号量可用于保护资源池。我们使用资源池来重用创建成本高的东西 - 例如数据库连接。
所以初始计数是指池中可用资源的数量
一些过程的开始。当您阅读代码中的initialCount
时,您应该考虑一下您在创建这个资源池时付出了多少前期努力。
我真的对初始计数的意义感到困惑?
Initial count
= Upfront cost
因此,根据您的应用程序的使用情况,该值可能会对您的应用程序的性能产生巨大影响。这不仅仅是一些任意的数字。
您应该仔细考虑您创建的内容、创建它们的成本以及您需要立即创建的数量。从字面上看,您应该能够绘制此参数的最佳值,并且应该考虑使其可配置,以便您可以使进程的性能适应它正在执行的时间。
【讨论】:
以上是关于信号量 - 初始计数有啥用?的主要内容,如果未能解决你的问题,请参考以下文章