Semaphore - What is the use of initial count?
Asked Answered
D

10

148

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 one 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
Disinfect answered 16/1, 2011 at 17:3 Comment(0)
S
127

So, I am really confused about the significance of initial count?

One important point that may help here is that Wait decrements the semaphore count and Release increments it.

initialCount is the number of resource accesses that will be allowed immediately. Or, in other words, it is the number of times Wait can be called without blocking immediately after the semaphore was instantiated.

maximumCount is the highest count the semaphore can obtain. It is the number of times Release can be called without throwing an exception assuming initialCount count was zero. If initialCount is set to the same value as maximumCount then calling Release immediately after the semaphore was instantiated will throw an exception.

Scoles answered 20/2, 2012 at 15:22 Comment(7)
This is so helpful! I had been thinking about Semaphores backward, as in initialCount being the number of initial BLOCKED resources, not the number of resources that are available immediately. Thank you.Doretha
@PhilipTenn, I agree - the documentation is not clear on this respectSwirly
I agreed, they should change that variable name or update the docsHaystack
@Disinfect you should accept this answer IMO, as it really explains the meaning of initialCount parameter.Pugging
I still don't get it to be honest. Under which circumstances would I call a Release without calling Wait before?Mcdonough
It's worth to mention also that from documentation, CurrentCount = Gets the number of remaining threads that can enter the Semaphore object. So, when they say count they mean "number of available spots", while I usually think at "number of threads that have entered" like you do when you get the count of a collection. They should really rename things here.Twosome
@Mcdonough About the usage of initial count: you set the initial count so that you allow the thread to enter immediately (initial count = total count) or to wait (initial count = 0) until you first initialize something else and then you explicitly allow the thread to enter changing the count with the Release method. Look at the example here: learn.microsoft.com/en-us/dotnet/api/…Twosome
C
111

Yes, when the initial number sets to 0 - all threads will be waiting while you increment the "CurrentCount" property. You can do it with Release() or Release(Int32).

Release(...) - will increment the semaphore counter

Wait(...) - will decrement it

You can't increment the counter ("CurrentCount" property) greater than maximum count which you set in initialization.

For example:

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()
Calculating answered 16/1, 2011 at 17:51 Comment(2)
Your code will be better presented in the answer rather than as a comment.Marplot
LOL it is probably the 5th time that I reach this same answer because the constructor's documentation always confuses me on which values to set. CheersSwirly
F
16

How many threads do you want to be able to access resource at once? Set your initial count to that number. If that number is never going to increase throughout the life of the program, set your max count to that number too. That way, if you have a programming error in how you release the resource, your program will crash and let you know.

(There are two constructors: one that takes only an initial value, and one that additionally takes the max count. Use whichever is appropriate.)

Farina answered 16/1, 2011 at 20:14 Comment(0)
E
11

Normally, when the SemaphoreSlim is used as a throttler, both initialCount and maxCount have the same value:

var semaphore = new SemaphoreSlim(maximumConcurrency, maximumConcurrency);

...and the semaphore is used with this pattern:

await semaphore.WaitAsync(); // or semaphore.Wait();
try
{
    // Invoke the operation that must be throttled
}
finally
{
    semaphore.Release();
}

The initialCount configures the maximum concurrency policy, and the maxCount ensures that this policy will not be violated. If you omit the second argument (the maxCount) your code will work just as well, provided that there are no bugs in it. If there is a bug, and each WaitAsync could be followed by more than one Release, then the maxCount will help at detecting this bug before it ends up in the released version of your program. The bug will be surfaced as a SemaphoreFullException, hopefully during the testing of a pre-release version, and so you'll be able to track and eliminate it before it does any real harm (before it has caused the violation of the maximum concurrency policy in a production environment).

The default value of the maxCount argument, in case you omit it, is Int32.MaxValue (source code).

Entirely answered 11/10, 2021 at 16:37 Comment(1)
Excellent answer, definitive!Hydroid
N
4

If you wish that no thread should access your resource for some time, you pass the initial count as 0 and when you wish to grant the access to all of them just after creating the semaphore, you pass the value of initial count equal to maximum count. For example:

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

As quoted in MSDN Documentation- "Another use of ReleaseSemaphore is during an application's initialization. The application can create a semaphore with an initial count of zero. This sets the semaphore's state to nonsignaled and blocks all threads from accessing the protected resource. When the application finishes its initialization, it uses ReleaseSemaphore to increase the count to its maximum value, to permit normal access to the protected resource."

Naked answered 20/2, 2012 at 13:31 Comment(2)
Sorry, I gave you the example in C++ though can clear the doubt.Naked
The quote about initialization is very helpful, although other answers explain how semaphoreSlim works, this is the only answer that has a very useful example use case for this somewhat obscure functionality.Isoagglutinin
F
2

This way when the current thread creates the semaphore it could claim some resources from the start.

Fabaceous answered 16/1, 2011 at 17:8 Comment(2)
So, you mean when I want two worker threads to access the resource I should change the initial count?Disinfect
No. It is the current thread that claims a count. If you do not want the current thread to claim any access pass 0 or use the overload with one parameter.Fabaceous
O
2

Think of it like this:

  • initialCount is the "degree of parallelism" (number of threads that can enter)
  • maxCount ensures that you don't Release more than you should

For example, say you want a concurrency degree of "1" (only one operation at a time). But then due to some bug in your code, you release the semaphore twice. So now you have a concurrency of two!

But if you set maxCount - it will not allow this and throw an exception.

Optimist answered 23/11, 2022 at 15:58 Comment(0)
H
0

maxCount is the number of concurrent threads that you're going to be allowing.

However, when you start the throttling, you may already know there are a few active threads, so you'd want to tell it "hey, I want to have 6 concurrent threads, but I already have 4, so I want you to only allow 2 more for now", so you'd set initialCount to 2 and maxCount to 6.

The limitation with initialCount in SemaphoreSlim is that it cannot be a negative number, so you can't say "hey, I want to have up to 6 concurrent threads, but I currently have 10, so let 5 get released before you allow another one in.". That would mean an initialCount of -4. For that you'd need to use a 3rd party package like SemaphoreSlimThrottling (note that I am the author of SemaphoreSlimThrottling).

Hurtless answered 2/5, 2022 at 7:3 Comment(2)
no, initialCount is the number of concurrent threads that can enter.Optimist
That's not right, @AlexfromJitbit. If you start with initialCount: 0 and maxCount: 2 it means that you can have up to 2 concurrent threads, but not until you first release. If you Release(2), then you immediately can allow 2 threads to enter, despite that initialCount was set to 0.Hurtless
S
-1

As MSDN explains it under the Remarks section:

If initialCount is less than maximumCount, the effect is the same as if the current thread had called WaitOne (maximumCount minus initialCount) times. If you do not want to reserve any entries for the thread that creates the semaphore, use the same number for maximumCount and initialCount.

So If the initial count is 0 and max is 2 it is as if WaitOne has been called twice by the main thread so we have reached capacity (semaphore count is 0 now) and no thread can enter Semaphore. Similarly If initial count is 1 and max is 2 WaitOnce has been called once and only one thread can enter before we reach capacity again and so on.

If 0 is used for initial count we can always call Release(2) to increase the semaphore count to max to allow maximum number of threads to acquire resource.

Sauceda answered 10/3, 2017 at 1:22 Comment(0)
B
-1

Semaphores can be used to protect a pool of resources. We use resource pools to reuse things that are expensive to create - such as database connections.

So initial count refers to the number of available resources in the pool at the start of some process. When you read the initialCount in code you should be thinking in terms of how much up front effort are you putting into creating this pool of resources.

I am really confused about the significance of initial count?

Initial count = Upfront cost

As such, depending on the usage profile of your application, this value can have a dramatic effect on the performance of your application. It's not just some arbitrary number.

You should think carefully about what you creating, how expensive they are to create and how many you need right away. You should literally able able to graph the optimal value for this parameter and should likely think about making it configurable so you can adapt the performance of the process to the time at which it is being executed.

Bridgeman answered 22/9, 2018 at 1:12 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.