Semaphore - What is the use of initial count?

http://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim.aspx

To create a semaphore, I need to provide an initial count and maximum count. MSDN states that an initial count is -

The initial number of requests for the semaphore that can be granted concurrently.

While it states that maximum count is

The maximum number of requests for the semaphore that can be granted concurrently.

I can understand that the maximum count is the maximum number of threads that can access a resource concurrently. But, what is the use of initial count?

If I create a semaphore with an initial count of 0 and a maximum count of 2, none of my threadpool threads are able to access the resource. If I set the initial count as 1 and maximum count as 2 then only thread pool thread can access the resource. It is only when I set both initial count and maximum count as 2, 2 threads are able to access the resource concurrently. So, I am really confused about the significance of initial count?

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
42067 次浏览

这样,当前线程创建信号量时,它可以从一开始就声明一些资源。

是的,当初始数字设置为0-所有线程将等待,而您增加“ CurrentCount”属性。您可以使用 Release()或 Release(Int32)进行此操作。

Release (...)-将增加信号量计数器

等待(...)-将减少它

不能将计数器(“ 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()

您希望同时访问多少个线程?将你的初始计数设置为那个数字。如果这个数字在程序的整个生命周期中都不会增加,那么把你的最大值也设置为这个数字。这样,如果在如何释放资源方面出现编程错误,程序将崩溃并通知您。

(有两个构造函数: 一个只接受初始值,另一个附加接受 max count。视乎情况而定。)

如果您希望在一段时间内没有任何线程访问您的资源,您可以将初始计数传递为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 将计数增加到最大值,以允许正常访问受保护的资源。”

那么,我真的对初始计数的意义感到困惑吗?

这里可能有所帮助的一个重要点是,Wait减少信号量计数,而 Release增加信号量计数。

initialCount是立即允许的资源访问次数。或者,换句话说,它是在信号量被实例化之后立即在不阻塞的情况下调用 Wait的次数。

maximumCount是信号量可以获得的最高计数。它是在假设 initialCount计数为零的情况下,在不抛出异常的情况下可以调用 Release的次数。如果将 initialCount设置为与 maximumCount相同的值,那么在信号量被实例化之后立即调用 Release将引发异常。

正如 MSDN在“备注”一节中解释的那样:

如果 initialCount 小于 maxumCount,则效果与当前线程调用 WaitOne (maxumCount 减去 initialCount)次数相同。如果不希望为创建信号量的线程保留任何条目,请对 maxumCount 和 initialCount 使用相同的数字。

因此,如果初始计数为0,max 为2,就好像 WaitOne 被主线程调用了两次,所以我们已经达到容量(信号量计数现在为0) ,而且没有线程可以进入 Semaphore。类似地,如果初始计数为1,最大值为2 WaitOnce 已被调用一次,并且在再次达到容量之前只有一个线程可以进入,以此类推。

如果初始计数使用0,我们总是可以调用 Release (2)将信号量计数增加到最大,以允许最大数量的线程获取资源。

可以使用信号量来保护资源池 。我们使用资源池来重用 造价昂贵的内容-比如数据库连接。

所以初始计数指的是 开始一些过程。当您阅读代码中的 initialCount时,您应该考虑在创建这个资源池之前投入了多少精力。

我真的对初始计数的意义感到困惑吗?

Initial count = Upfront cost

因此,根据应用程序的使用情况,此值可能会对应用程序的性能产生显著影响。这不是什么随意的数字。

您应该仔细考虑您创建的内容,创建它们的成本有多高,以及您现在需要多少。您应该能够为这个参数绘制最佳值,并且应该考虑使其可配置,这样您就可以根据流程的执行时间来调整流程的性能。

通常,当 SemaphoreSlim用作节流阀时,initialCountmaxCount的值是相同的:

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) ,只要没有错误,代码也能正常工作。如果存在一个 bug,并且每个 WaitAsync后面可能跟着一个以上的 Release,那么 maxCount将有助于在这个 bug 最终出现在您的程序的发布版本之前检测到它。这个 bug 将以 SemaphoreFullException的形式出现,希望是在测试预发布版本期间出现,这样您就能够在它造成任何实际损害之前(在它在生产环境中导致违反最大并发策略之前)跟踪并消除它。

如果省略了 maxCount参数,那么它的默认值是 Int32.MaxValue(源代码)。

maxCount是您将允许的并发线程数。

然而,当你开始节流时,你可能已经知道有几个活动线程,所以你想告诉它“嘿,我想有6个并发线程,但是我已经有4个了,所以我希望你现在只允许多2个”,所以你会设置 initialCount为2,maxCount为6。

SemaphoreSliminitialCount的局限性在于它不能是一个负数,所以你不能说“嘿,我想要多达6个并发线程,但是我现在有10个,所以在允许另一个线程进入之前,先释放5个线程。”.那意味着 initialCount是 -4。为此,您需要使用第三方软件包,如 信号减速(请注意,我是 SemaphoreSlimThrotling 的作者)。

你可以这样想:

  • initialCount是“并行度”(可以输入的线程数)
  • maxCount确保你不会超过你应该的 Release

例如,假设您希望并发度为“1”(一次只有一个操作)。但是由于代码中的一些 bug,您释放了两次信号量。所以现在您有了两个并发!

但是,如果你设置 maxCount-它将不允许这样,并抛出一个异常。